me again. Sorry about this.
Following on from my previous question (I think I failed to adequately demonstrate the source of my confusion), here is the actual function I was writing:
/// <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));
}
}
I am not sure I see why the explicit cast to TNode is needed here:
nodeQueue.Enqueue(new Tuple<TNode, int>((TNode)parentNode.Left, nodeDepth));
Under what circumstances could parentNode.Left be something that is not assignable to TNode (given TNode is constrained to be of type BTreeNode or derived).
To put the question another way, under what circumstances could this function result in an InvalidCastException? If there isn’t one, then why does the compiler require an explicit cast?
Edit: I think I’m ok with changing the implementation to something like:
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));
parentNode.Leftis only typed to beBTreeNode. There’s no guarantee that it’s the same asTNode. Imagine you had:Now consider
TraverseBreadthFirst<SpecialBTreeNode>(rootNode, ...). What’s to stoprootNode.Leftfrom returning aBoringBTreeNode?It sounds like you might want to make
BTreeNodeitself generic: