c#为什么在以这种方式处理类型参数时需要显式强制转换?

本文关键字:转换 类型参数 为什么 处理 方式 | 更新日期: 2023-09-27 18:11:08

又是

我。很抱歉。

根据我之前的问题(我认为我没有充分展示我困惑的根源),下面是我正在编写的实际函数:

/// <summary>
///     A b-tree node.
/// </summary>
public class BTreeNode
{
    /// <summary>
    ///     Create a new b-tree node.
    /// </summary>
    public BTreeNode()
    {
    }
    /// <summary>
    ///     The node name.
    /// </summary>
    public string Name
    {
        get;
        set;
    }
    /// <summary>
    ///     The left-hand child node.
    /// </summary>
    public BTreeNode Left
    {
        get;
        set;
    }
    /// <summary>
    ///     The right-hand child node.
    /// </summary>
    public BTreeNode Right
    {
        get;
        set;
    }
}
/// <summary>
///     Perform a breadth-first traversal of a b-tree.
/// </summary>
/// <param name="rootNode">
///     The b-tree's root node.
/// </param>
/// <param name="forEachNode">
///     An action delegate to be called once for each node as it is traversed.
///     Also includes the node depth (0-based).
/// </param>
public static void TraverseBreadthFirst<TNode>(this TNode rootNode, Action<TNode, int> forEachNode)
    where TNode : BTreeNode
{
    if (rootNode == null)
        throw new ArgumentNullException("rootNode");
    if (forEachNode == null)
        throw new ArgumentNullException("forEachNode");
    Queue<Tuple<TNode, int>> nodeQueue = new Queue<Tuple<TNode, int>>(3); // Pretty sure there are never more than 3 nodes in the queue.
    nodeQueue.Enqueue(new Tuple<TNode, int>(rootNode, 0));
    while (nodeQueue.Count > 0)
    {
        Tuple<TNode, int> parentNodeWithDepth = nodeQueue.Dequeue();
        TNode parentNode = parentNodeWithDepth.Item1;
        int nodeDepth = parentNodeWithDepth.Item2;
        forEachNode(parentNode, nodeDepth);
        nodeDepth++;
        if (parentNode.Left != null)
            nodeQueue.Enqueue(new Tuple<TNode, int>((TNode)parentNode.Left, nodeDepth));
        if (parentNode.Right != null)
            nodeQueue.Enqueue(new Tuple<TNode, int>((TNode)parentNode.Right, nodeDepth));
    }
}

我不确定我看到为什么这里需要显式转换到TNode:

nodeQueue.Enqueue(new Tuple<TNode, int>((TNode)parentNode.Left, nodeDepth));

在什么情况下parentNode。

如果TNode被限制为BTreeNode类型或派生类型,则为不可赋值给TNode。

换句话说,在什么情况下这个函数会导致InvalidCastException?如果没有,那么为什么编译器需要显式强制转换?

编辑:我想我可以将实现更改为:

TNode leftNode = parentNode.Left as TNode;
Debug.Assert(leftNode != null || parentNode.Left == null, "Left child is more derived.");
if (leftNode != null)
    nodeQueue.Enqueue(new Tuple<TNode, int>(leftNode, nodeDepth));

c#为什么在以这种方式处理类型参数时需要显式强制转换?

parentNode.Left只输入为BTreeNode。不能保证它与TNode相同。假设你有:

class SpecialBTreeNode : BTreeNode
class BoringBTreeNode : BTreeNode

现在考虑TraverseBreadthFirst<SpecialBTreeNode>(rootNode, ...)。如何阻止rootNode.Left返回BoringBTreeNode ?

// This is entirely valid...
SpecialBTreeNode special = new SpecialBTreeNode();
special.Left = new BoringBTreeNode();

听起来你可能想让BTreeNode本身通用:

public class BTreeNode<T> where T : BTreeNode<T>
{
    public T Left { get; set; }
    public T Right { get; set; }
}

您的parentNode.Left被定义为BTreeNode,而不是TNode。即使您派生了TNode:BTreeNode,您的.Left仍然是BTreeNode的引用,而不是TNode。所以你必须投。您需要BTreeNode类是通用的,正如Jon Skeet指出的。