开发者

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 Lists 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜