c#中带有方差的泛型方法
本文关键字:泛型方法 方差 | 更新日期: 2023-09-27 17:52:38
考虑以下类(继承树):
public class A {}
public class B: A {}
这个方法:
public IList<A> MyMethod(){
IList<B> result = new List<B>();
//add some items to result
return result;
}
编译器不高兴。错误是Cannot convert expression type IList<B> to return type IList<A>
。怎么解呢?换句话说,如何指定MyMethod
将返回T
的IList<T>
,其中T
可以是继承自A
或A
本身的任何实例?
您所要求的是不可能的,因为IList<T>
不支持方差—您不能在期望IList<A>
的任何地方使用IList<B>
。为了想出一个解决方案,你必须更详细地解释你想要什么。
可能的解决方案如下:
public IList<A> MyMethod(){
IList<A> result = new List<A>();
//add some items to result
return result;
}
或
public IEnumerable<A> MyMethod(){
IList<B> result = new List<B>();
//add some items to result
return result;
}
不能转换IList到IList,即使B继承自A,否则,用户可能会尝试将A的非B实例添加到列表中。
public void Example(){
IList<B> listB = new List<B>();
IList<A> listA = listB;
listA.Add(new A()); // Can't insert A into a list of B
}
能否返回IEnumerable而不是IList?IEnumerable是协变的,不像IList
如何指定MyMethod将返回T的IList,其中T可以是继承自A或A本身的实例的任何东西?
你不需要
你可以声明它返回IList<A>
。为什么?因为——假定B
继承自A
——B
的每一项都可以传递到所需类型为A
的地方。
可以称其为继承多态性、Liskov替代原则或方法方差,名称无关紧要。重要的是,以下工作(在LinqPad上测试):
public class A {}
public class B: A {}
public IList<A> MyMethod()
{
var result = new List<A>();
//add some items to result
result.Add(new B());
return result;
}
遗传选择
事实上,您可以看出您将返回一个IList<TA>
并请求一些派生类型(TB
, TC
…)来填充它。没错,下面的例子也可以工作(在LinqPad上测试):
void Main()
{
MyMethod<A, B, C>();
}
public class A {}
public class B: A {}
public class C: A {}
public IList<TA> MyMethod<TA, TB, TC>()
where TB : TA, new()
where TC : TA, new()
where TA : class
{
var result = new List<TA>();
//add some items to result
result.Add(new B() as TA);
result.Add(new C() as TA);
return result;
}
或者如果你想保留一个特定的基类型(比如你想返回一个IList<A>
,但它实际上包含了从A
派生的类的项),那么你可以这样做:
void Main()
{
MyMethod<B, C>();
}
public class A {}
public class B: A {}
public class C: A {}
public IList<A> MyMethod<T1, T2>()
where T1 : A, new()
where T2 : A, new()
{
var result = new List<A>();
//add some items to result
result.Add(new T1() as A);
result.Add(new T2() as A);
return result;
}
你不需要,但是你可以
好的,如果你真的想说它返回IList<T>
where T : A
。那就说出来!
void Main()
{
MyMethod<B>();
}
public class A {}
public class B: A {}
//public class C: A {} //Even if I don't add that class
public IList<T> MyMethod<T>()
where T : A, new()
{
var result = new List<T>();
//add some items to result
result.Add(new T());
return result;
}
是的,它不能返回类型为T
和类型为A
的项目的混合,因为它说它返回IList<T>
,而不是每个类型为A
的项目也都是类型为T
的项目。
你的代码发生了什么
查看你的代码:
public IList<A> MyMethod(){
IList<B> result = new List<B>();
//add some items to result
return result;
}
当你说你要返回IList<A>
时,你试图返回IList<B>
。让我们假设这行得通……那么你的方法的调用者会发生什么?让我们看看:
public class A {}
public class B: A {}
public class C: A {}
void Main()
{
//Hmmm... I need a IList<T>, let's call MyMethod!
IList<A> list = MyMethod();
//Cool, I got an IList<A>, now let's add some items...
var item = new C();
//Well, item is of type C...
// and C inherits from A, so I must be able to add it...
list.Add(item); //BOOM!
//It was actually an IList<B>!
// and C doesn't dervive from B, so you can't add it.
}
DFTBA !