泛型铸造
问题描述:
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...
使用泛型时等看来,编译器将不会在 一般类型投下内通用类型(基本/子)(OtherBase/OtherSub)。为什么会发生?泛型铸造
更新: 还请解释一下上面的之间的区别如下(工作)
void Call<T>() where T : Base { }
//...
Call<Sub>();
答
险要这种行为(称为“通用方”)是必要的,否则下面的代码会编译:
List<string> strlist = new List<string>();
List<object> objlist = strlist;
objlist.Add(42);
我们已经在字符串列表中添加了一个数字。不好。 (顺便说一下,代码编译阵列而不是List
是因为Java的允许这种出于某种原因;但是,这将引发一个运行时异常。)
你可以在你的情况下避免这种情况,但:
static void Call<U, T>(T x) where U : Base where T : OtherBase<U> { }
,并调用它是这样的:
Call(new OtherSub<Sub());
C#4.0还提供generic variance for interfaces。但是,它们的使用往往不是必需的。
答
您的问题与称为方差/协方差的概念有关。实际上,如果A
继承自B
,Class<A>
不是Class<B>
。
见下面的例子:
Class<T>
暴露的公共方法foo(T param)
如果Class<A>
是Class<B>
,然后不必Class<B>
参考作为Class<A>
和调用foo(B param)
(具有B
实例)的方法,将是呼吁foo(A param)
。而B
不是A
。
实际上,仅当T
仅用作Class<T>
中的返回值时,Class<A>
才能从Class<B>
继承。
这是在.NET 4中通过泛型接口的out关键字实施的。因此Class<T>
可以实现IClass<out T>
。
答
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>>()
会工作,然后。
这对于给出的示例有很大的意义,但仍然不适用于Call方法。你能否解释一下你的例子和“void Call()其中T:Base {}”,“请致电()”的区别? –
jameszhao00
2011-06-11 15:28:22
@james真的,我的错误。修正了。您的扩展问题与通用差异完全无关,因此没有理由不适用。 – 2011-06-11 15:34:20