Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics Casting

Tags:

c#

generics

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>();
like image 924
jameszhao00 Avatar asked Dec 17 '22 13:12

jameszhao00


2 Answers

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.

like image 148
Konrad Rudolph Avatar answered Jan 03 '23 22:01

Konrad Rudolph


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

like image 34
Eilistraee Avatar answered Jan 03 '23 21:01

Eilistraee