2011-10-13 34 views
1

我再次。为此事道歉。C#泛型 - 以这种方式处理类型参数时为什么需要显式强制转换?

从我previous question(我想我没能充分展示我混乱的根源),这里是实际功能我写继:

/// <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.Left是东西,是不能分配给TNODE(给出TNODE被约束型BTreeNode或派生的)。

换句话说,这个函数在什么情况下会导致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)); 
+0

刚刚意识到了这个问题。回想起来似乎很明显! – tintoy

回答

4

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; } 
} 
+0

你知道,只要我提交,我意识到这一点: - # – tintoy

+0

(忏悔调试) – tintoy

0

parentNode.Left被定义为BTreeNode,而不是TNode。即使你得到TNode:BTreeNode,你的.Left仍然是BTreeNode的参考,而不是TNode之一。所以你必须施放。 Jon Skeet指出,您需要BTreeNode类才是通用的。

+0

谢谢,是的 - 我会修改设计考虑到这一点。 – tintoy

相关问题