I honestly thought I was done with this topic but as it turns out I missed
something. I stated firmly that "currently, in C#, we are stuck with the cast"
but I was wrong. One of the great things about working at Microsoft is the people you can interact with. In
back burner researching a definitive answer to Hallvard's
question to my post of why linked list LinkedList<T> cannot be written,
class LinkedList<T>: LinkedList<T, LinkedList<T>.Node> { }
I asked Andrew Kennedy to look at my page.
I like talking to Andrew; I learn at least one new thing every time we talk.
This was certainly no exception. He quickly pointed out something
that was staring me squarely in the face but I missed entirely, folding my implementation of
DoubleLinkedList<T> with DoubleLinkedList<T, N>
and closing the node type directly
will remove the cast. In other words, DoubleLinkedList<T> can be written without
casts as,
class DoubleLinkedList<T>: LinkedList<T, DoubleLinkedList<T>.DoubleNode>,
IDoubleLinkedList<T> {
publicclass DoubleNode: Node {
DoubleNode _previous;
publicoverride DoubleNode Next {
get {
returnbase.Next;
}
set {
base.Next = value;
value._previous = this;
}
}
public DoubleNode Previous { get { return _previous; } }
}
public IEnumerable<T> Backwards {
get {
if (Last != null) {
DoubleNode current = Last;
do {
yieldreturn current.Data;
current = current.Previous;
} while (current != Last);
}
}
}
}
which closes the node parameter instead of leaving it open. This is one of
those head-slapping, "how stupid of me" moments like when someone points out
that "you know, if you would have pushed the other pawn instead, you would have
had checkmate in three moves." You can see that this is a straight forward
application of the requires clause version (declaring it to be
DoubleNode instead of requiring it). In my zest to introduce the concepts of the "this type" from LOOM and the
requires clause from Scala, I missed what should have been obvious.
Silly me.
This solution has the limitation that you cannot derive something like
TripleLinkedList<T> from
DoubleLinkedList<T> so my example still holds some water (TripleLinkedList<T,
N> can be derived from DoubleLinkedList<T, N>)
but it is not nearly so compelling. I still think that one of the two features
would be very useful but I need a much better motivating example.
While I still don't have a definitive answer to Hallvard's question, I picked
up this gem along the way. Not bad so far.
That's interesting! Great way to get more type safe and explicit code (even though it make the class less re-inheritable, as you say).
Sounds a bit weird that the compiler accepts (simplified from your example above):
class DoubleLinkedList<T>: LinkedList<T, DoubleLinkedList<T>.DoubleNode> { }
but not:
class LinkedList<T>: LinkedList<T, LinkedList<T>.Node> { }
Maybe it somehow thinks that you are using the parent class to define the parent class in some recursive and thus illegal way? I don't think you are in either of the cases.
Thanks for the posts, Chuck!
btw, completely unrelated, but do you have any knowledge about the MILcore API (that is a native API between DirectX and the unmanaged WPF (née Avalon) classes)? Are they or will they be publically available and documented?
The reason I'm asking is that native development tools (such as Delphi and C++) could have a real boost if they could take advantage of the advanced graphics and composition features you'd be able to create some stunning application without being forced to used managed code. I know Microsoft wants everybody and their mother to use managed code, but sometime it just isn't the right solution and even MS uses mainly native code in Vista itself, AFAIK.
As I said, I still don't have a definitive answer for this. I will post something when I do. I believe it has something to do with using a symbol from the base type before the base type is fully defined, or, more correctly, using from a symbol from the base type to define the base type. In DoubleNode version, it is a symbol from the current type, not from a only partially defined base type. But, for now, this is just a guess. I have it as a low priority investigation since there is such an obvious work-around. I see it more of a curiosity than a problem.
As for the MIL and MILCORE, I am certainly not the one to ask. You should check out Greg Schechter's Blog (http://blogs.msdn.com/greg_schechter). He led the effort to produce the DWM and is a lead architect on the WPF.
Linked List V: Revenge of the Type Parameter
I honestly thought I was done with this topic but as it turns out I missed something. I stated firmly that "currently, in C#, we are stuck with the cast" but I was wrong. One of the great things about working at Microsoft is the people you can interact with. In back burner researching a definitive answer to Hallvard's question to my post of why linked list LinkedList<T> cannot be written,
class LinkedList<T>: LinkedList<T, LinkedList<T>.Node> { }I asked Andrew Kennedy to look at my page. I like talking to Andrew; I learn at least one new thing every time we talk. This was certainly no exception. He quickly pointed out something that was staring me squarely in the face but I missed entirely, folding my implementation of DoubleLinkedList<T> with DoubleLinkedList<T, N> and closing the node type directly will remove the cast. In other words, DoubleLinkedList<T> can be written without casts as,
class DoubleLinkedList<T>: LinkedList<T, DoubleLinkedList<T>.DoubleNode>, IDoubleLinkedList<T> { public class DoubleNode: Node { DoubleNode _previous; public override DoubleNode Next { get { return base.Next; } set { base.Next = value; value._previous = this; } } public DoubleNode Previous { get { return _previous; } } } public IEnumerable<T> Backwards { get { if (Last != null) { DoubleNode current = Last; do { yield return current.Data; current = current.Previous; } while (current != Last); } } } }which closes the node parameter instead of leaving it open. This is one of those head-slapping, "how stupid of me" moments like when someone points out that "you know, if you would have pushed the other pawn instead, you would have had checkmate in three moves." You can see that this is a straight forward application of the requires clause version (declaring it to be DoubleNode instead of requiring it). In my zest to introduce the concepts of the "this type" from LOOM and the requires clause from Scala, I missed what should have been obvious. Silly me.
This solution has the limitation that you cannot derive something like TripleLinkedList<T> from DoubleLinkedList<T> so my example still holds some water (TripleLinkedList<T, N> can be derived from DoubleLinkedList<T, N>) but it is not nearly so compelling. I still think that one of the two features would be very useful but I need a much better motivating example.
While I still don't have a definitive answer to Hallvard's question, I picked up this gem along the way. Not bad so far.
12:37 PM | Comments [2] | #Programming
09/15/2006 2:34 AM
Hi Chuck,That's interesting! Great way to get more type safe and explicit code (even though it make the class less re-inheritable, as you say).
Sounds a bit weird that the compiler accepts (simplified from your example above):
class DoubleLinkedList<T>: LinkedList<T, DoubleLinkedList<T>.DoubleNode> { }
but not:
class LinkedList<T>: LinkedList<T, LinkedList<T>.Node> { }
Maybe it somehow thinks that you are using the parent class to define the parent class in some recursive and thus illegal way? I don't think you are in either of the cases.
Thanks for the posts, Chuck!
btw, completely unrelated, but do you have any knowledge about the MILcore API (that is a native API between DirectX and the unmanaged WPF (née Avalon) classes)? Are they or will they be publically available and documented?
The reason I'm asking is that native development tools (such as Delphi and C++) could have a real boost if they could take advantage of the advanced graphics and composition features you'd be able to create some stunning application without being forced to used managed code. I know Microsoft wants everybody and their mother to use managed code, but sometime it just isn't the right solution and even MS uses mainly native code in Vista itself, AFAIK.
Hallvard Vassbotn | http://hallvards.blogspot.com/
09/15/2006 10:10 AM
As I said, I still don't have a definitive answer for this. I will post something when I do. I believe it has something to do with using a symbol from the base type before the base type is fully defined, or, more correctly, using from a symbol from the base type to define the base type. In DoubleNode version, it is a symbol from the current type, not from a only partially defined base type. But, for now, this is just a guess. I have it as a low priority investigation since there is such an obvious work-around. I see it more of a curiosity than a problem.As for the MIL and MILCORE, I am certainly not the one to ask. You should check out Greg Schechter's Blog (http://blogs.msdn.com/greg_schechter). He led the effort to produce the DWM and is a lead architect on the WPF.
Chuck Jazdzewski | removingalldoubt.com | chuckjAT NOSPAMmicrosoft dot com