简体   繁体   中英

Generics Casting

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM