泛型转换

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

似乎在使用泛型时,编译器不会将内部泛型类型(Base/Sub)转换为泛型类型(OtherBase/OtherSub)。为什么会发生这种情况?
更新:请解释一下上述内容和以下内容之间的区别(以下内容可以正常工作)。
void Call<T>() where T : Base { }
//...
Call<Sub>();
3个回答

8
禁止这种行为(称为“通用变异”)是必要的,因为否则以下代码将会编译通过:
List<string> strlist = new List<string>();
List<object> objlist = strlist;
objlist.Add(42);

我们向字符串列表中添加了一个数字,这并不好。顺便说一下,如果使用数组而不是列表,代码会编译通过,因为Java出于某种原因允许这样做;但是,这将引发运行时异常。

不过,在您的情况下可以避免这种情况:

static void Call<U, T>(T x) where U : Base where T : OtherBase<U> { }

然后这样调用:

Call(new OtherSub<Sub());

C# 4.0还为接口提供了泛型变量,但通常情况下并不需要使用它们。

这个例子很有道理,但对于方法Call仍然不清楚。您能否解释一下您的示例和“void Call<T>() where T : Base { }”、“Call<Sub>()”之间的区别呢? - jameszhao00
@james 是的,我的错误。已经修复了。你扩展的问题与通用变异完全无关,所以这应该是可行的。 - Konrad Rudolph


1
Konrad有一个关于如何修复您的代码的好建议。如果您想要使用C# 4的方差,可以像这样做:
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>>() 然后就可以工作了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接