2011-10-13 83 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

相關問題