[英]Casting List<> of Derived class to List<> of base class
I have two classes: a base class (Animal) and a class deriving from it (Cat).Base class contains one virtual method Play that takes List as input parameter.Something like this我有两个类:一个基类(Animal)和一个派生自它的类(Cat)。基类包含一个虚拟方法 Play,它将 List 作为输入参数。类似这样的东西
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication9
{
class Animal
{
public virtual void Play(List<Animal> animal) { }
}
class Cat : Animal
{
public override void Play(List<Animal> animal)
{
}
}
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat();
cat.Play(new List<Cat>());
}
}
}
When i compile the above program,i get the following error当我编译上述程序时,我收到以下错误
Error 2 Argument 1: cannot convert from 'System.Collections.Generic.List' to 'System.Collections.Generic.List'
Is there anyway to accomplish this?有没有办法做到这一点?
The reason you cannot do this is because a list is writable.你不能这样做的原因是列表是可写的。 Suppose it were legal, and see what goes wrong:假设它是合法的,看看出了什么问题:
List<Cat> cats = new List<Cat>();
List<Animal> animals = cats; // Trouble brewing...
animals.Add(new Dog()); // hey, we just added a dog to a list of cats...
cats[0].Speak(); // Woof!
Well dog my cats, that is badness.好吧,狗我的猫,那是坏事。
The feature you want is called "generic covariance" and it is supported in C# 4 for interfaces that are known to be safe.您想要的功能称为“通用协方差”,C# 4 支持已知安全的接口。 IEnumerable<T>
does not have any way to write to the sequence, so it is safe. IEnumerable<T>
没有任何方法可以写入序列,因此它是安全的。
class Animal
{
public virtual void Play(IEnumerable<Animal> animals) { }
}
class Cat : Animal
{
public override void Play(IEnumerable<Animal> animals) { }
}
class Program
{
static void Main()
{
Cat cat = new Cat();
cat.Play(new List<Cat>());
}
}
That will work in C# 4 because List<Cat>
is convertible to IEnumerable<Cat>
, which is convertible to IEnumerable<Animal>
.这将在 C# 4 中起作用,因为List<Cat>
可转换为IEnumerable<Cat>
,后者可转换为IEnumerable<Animal>
。 There is no way that Play can use IEnumerable<Animal>
to add a dog to something that is actually a list of cats. Play 无法使用IEnumerable<Animal>
将狗添加到实际上是猫列表的内容中。
You could do a few things.你可以做几件事。 One example is cast the elements of the list to Animal
一个例子是将列表的元素转换为Animal
Using your code:使用您的代码:
cat.Play(new List<Cat>().Cast<Animal>().ToList());
Another is to make Animal
generic, so cat.Play(new List<Cat>());
另一个是使Animal
通用,所以cat.Play(new List<Cat>());
would work.会工作。
class Animal<T>
{
public virtual void Play(List<T> animals) { }
}
class Cat : Animal<Cat>
{
public override void Play(List<Cat> cats)
{
}
}
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat();
cat.Play(new List<Cat>());
}
}
One other method is to not make Animal
generic, but the Play
method and constrain that to T : Animal
另一种方法是不使Animal
泛型,而是使Play
方法并将其限制为T : Animal
class Animal
{
public virtual void Play<T>(List<T> animals) where T : Animal { }
}
class Cat : Animal
{
public override void Play<T>(List<T> animals)
{
}
}
Finally, if you are on C# 4 and only need to enumerate over the list and not modify it, check Eric Lippert's answer on IEnumerable<Animal>
.最后,如果您使用 C# 4 并且只需要枚举列表而不修改它,请查看 Eric Lippert 在IEnumerable<Animal>
上的回答。
You're looking for generic collection covariance.您正在寻找通用集合协方差。 Obviously, though, that feature is not supported by the version of C# that you're using.但显然,您使用的 C# 版本不支持该功能。
You can work around it by using the Cast<T>()
extension method.您可以使用Cast<T>()
扩展方法来解决它。 Be aware, though, that this will create a copy of your original list instead of passing the original as a different type:但是请注意,这将创建原始列表的副本,而不是将原始列表作为不同的类型传递:
cat.Play((new List<Cat>()).Cast<Animal>().ToList());
use the extension method Cast()使用扩展方法 Cast()
so:所以:
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat();
cat.Play(new List<Cat>().Cast<Animal>());
}
}
The reason for this is b/c .net 3.5 does not support covariance, but 4.0 does :)原因是 b/c .net 3.5 不支持协方差,但 4.0 支持:)
Everyone mentions the cast method already.每个人都已经提到了 cast 方法。 If you can not update to 4.0 a way to hide the cast is如果你不能更新到 4.0 隐藏演员的方法是
class Cat : Animal
{
public override void Play(List<Animal> animal)
{
Play((List<Cat>)animal);
}
public virtual void Play(List<Cat> animal)
{
}
}
This is the same trick IEnumable
and IEnumarable<T>
play for GetEnumerator这与IEnumable
和IEnumarable<T>
为 GetEnumerator 玩的技巧相同
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.