What was the thinking behind the design decision to not implement variance on generic class types? Is it not possible, undesirable or is it on a 'maybe one day' list?
You are in good company; Jon Skeet and Bill Wagner asked me that same question a couple weeks ago. I have been working up a blog post on it, but briefly:
The right person to ask for a definitive answer is Andrew Kennedy at Microsoft Research Cambridge, who designed and implemented much of the generic and variance logic originally. However, I can hazard an educated guess as to why we decided to eschew variance on generic classes.
The short version is: Safe covariance of T requires that operations on T be "read only". Contravariance of T requires that operations on T be "write only". You have a class C<T>
which you wish to be variant in T. Suppose `C has a field of type T: would you like to have that field be only readable or only writable? Because those are your choices!
Under what circumstances is it even vaguely useful to have a field that can be written to but not read? Not many. Under what circumstances is it useful to have a field that can be read but not written to? Only if the field is marked readonly.
In short, contravariant generic classes are almost never useful because you can't read any generic data from them, and covariant classes are mostly only useful if the class is an immutable data type.
I am a big fan of immutable data types and I think it would be a great feature to be able to make a covariant immutable stack without having to get an interface involved. But covariant generic persistent immutable functional data structures are not exactly mainstream in C# yet, and they certainly were not when generics were added to the CLR. Moreover, we have no supporting infrastructure other than readonly fields to express the notion "this is an immutable data type" in C# or the CLR; if we were to do covariant class types for immutable classes, it would be nice to do lots of features that support immutable classes, not just this obscure one.
So I can see how this feature did not make the cut in CLR 2.0 / C# 2.0. If we were designing it again today, when functional-style programming is somewhat more popular, maybe it would. But we have no plans to do so any time soon.
I'll write a blog post in the next few months giving a more detailed answer.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…