Well, when you write a class C<T>
, it is all about C
, not T
. T
is merely a type info provided to denote a special C<>
type. C<>
has its own implementation and has no relation at all with T
. When you write:
var c = new C<int>();
the behaviour of variable c
is all in accordance with what you have written in class C<>
. It behaves the same whether T
is int
or string
or anything.
Also note that a class written like C<S>
is a special type and different from C<T>
which is another type, because all the behaviour is confined by it's C
-ness. We never know how is C<>
written and how it will behave. So the way you have described is not how generics play out. If you have an additional class like
class D<T> : C<T>
{
}
you can write:
C<A> c = new D<A>();
You see, it is again possible because C
derives from D
. The left side of the class matters. This is again illegal:
C<A> c = new D<B>();
even though A
derives from B
, because C
of A
has no relationship with D
of B
(or even C
of B
) which is a separate class.
This kind of generic polymorphism is actually meaningless when you think C<A>
as an entirely different type to C<B>
though they have a common behaviour. To illustrate with another example, think about this, a class can be generic by more than one type. Suppose there is
class C<S, T>
{
}
What about C<IList<A>, A>
and C<IList<int>, B>
? Are they type comparable because those two have IList
and A
and B
as generic types? Is C<T>
castable from C<S, T>
? No these are different types in short.
Now if you wan't to make the assignment
C<A> ca = new C<B>();
possible you can write your own implicit converters.
class C<T>
{
public static implicit operator C<T>(C<B> c)
{
type check, explode or return
}
}
//now you may write:
C<A> ca = new C<B>();
//or even
C<A> ca = new D<B>();
Mind you this does not imply inheritance relationship, but merely a conversion operation.
And no information is complete on generics without information on covariance and contravariance (which is applicable only for interfaces and delegates). See O. R. Mapper's answer as to the dangers if this was let to happen. The one case where C# lets this is in case of arrays. And sadly that's a broken design. This is possible in C#
object[] o = new string[1];
o[0] = 1; //explosion
I'm tempted to detail out, but anything I do would make it redundant. See this excellent answer by Eric Lippert for a clear info.