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>();
答案 0 :(得分:8)
禁止此行为(称为“通用差异”)是必要的,否则以下代码将编译:
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。但是,它们的使用通常不是必需的。
答案 1 :(得分:3)
您的问题与称为差异/协方差的概念相关联。事实上,如果A
继承自B
,则Class<A>
不是Class<B>
。
见这个例子:
Class<T>
公开了一种公开方法foo(T param)
如果Class<A>
是Class<B>
,则引用Class<B>
作为Class<A>
并调用foo(B param)
(B
的方法} instance)将调用foo(A param)
。而B
不是A
。
事实上,Class<A>
只有在Class<B>
仅将T
用作返回值时才能从Class<T>
继承。
这在.NET 4中通过泛型接口的out关键字强制执行。因此Class<T>
可以实施IClass<out T>
。
答案 2 :(得分: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>>()
会起作用。