Generics Casting
开发者_如何学Cinterface Base { ... }
class Sub : Base { ... }
class OtherBase<T> where T : Base { ... }
class OtherSub<T> : OtherBase<T> where T : Base { ... }
//...in some class
void Call<T>() where T : OtherBase<Base> { }
//...
Call<OtherSub<Sub>>(); //compile fails...
Seems like when using generics, the compiler won't cast a inner generic type (Base/Sub) in the generic type (OtherBase/OtherSub). Why does this happen?
Update: Please also explain the difference between the above and the following (which works)
void Call<T>() where T : Base { }
//...
Call<Sub>();
Forbidding this behaviour (known as “generic variance”) is necessary because otherwise the following code would compile:
List<string> strlist = new List<string>();
List<object> objlist = strlist;
objlist.Add(42);
We’ve added a number to a list of strings. Not good. (Incidentally, the code would compile for arrays instead of List
s because Java allowed this for some reason; however, this will raise a runtime exception.)
You can avoid this in your case though:
static void Call<U, T>(T x) where U : Base where T : OtherBase<U> { }
And call it like this:
Call(new OtherSub<Sub());
C# 4.0 furthermore provides generic variance for interfaces. However, their use isn’t often necessary.
Your issue is linked to a concept called variance/covariance. In fact, if A
inherits from B
, Class<A>
isn't a Class<B>
.
See this example:
Class<T>
exposes a public method foo(T param)
If Class<A>
was a Class<B>
, then a method having a reference to Class<B>
as a Class<A>
and calling foo(B param)
(with a B
instance) would be calling foo(A param)
. And B
isn't a A
.
In fact, Class<A>
can inherit from Class<B>
only if T
is used as a return value only in Class<T>
.
This is enforced in .NET 4 through the out keyword for generics interface. Class<T>
could therefore implement IClass<out T>
.
Konrad has a good advice on how to fix your code. If you wanted to use C# 4's variance, you could do it like this:
interface IOtherBase<out T> where T : Base { }
class OtherBase<T> : IOtherBase<T> where T : Base { }
class OtherSub<T> : OtherBase<T> where T : Base { }
static void Call<T>() where T : IOtherBase<Base> { }
Call<OtherSub<Sub>>()
would work then.
精彩评论