将基类的派生类转换为列表<>
我有两个类:一个基类(Animal)和一个从它派生的类(Cat).Base类包含一个虚拟方法Play,它将List作为input参数。像这样的东西
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>()); } } }
当我编译上面的程序,我得到以下错误
错误2参数1:无法从“System.Collections.Generic.List”转换为“System.Collections.Generic.List”
有没有办法做到这一点?
你不能这样做的原因是因为列表是可写的。 假设这是合法的,看看出了什么问题:
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!
好狗我的猫,那就是坏话。
您想要的function称为“通用协方差”,并且在C#4中为已知安全的接口提供支持。 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>()); } }
这将在C#4中工作,因为List<Cat>
可以转换为IEnumerable<Cat>
,它可以转换为IEnumerable<Animal>
。 Play无法使用IEnumerable<Animal>
将狗添加到实际上是猫列表的东西上。
你可以做一些事情。 一个例子是将列表的元素投入到Animal
使用你的代码:
cat.Play(new List<Cat>().Cast<Animal>().ToList());
另一个是使Animal
通用,所以cat.Play(new List<Cat>());
会工作。
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>()); } }
另一种方法是不要使Animal
通用,但是对于T : Animal
, Play
方法和限制
class Animal { public virtual void Play<T>(List<T> animals) where T : Animal { } } class Cat : Animal { public override void Play<T>(List<T> animals) { } }
最后,如果你使用C#4,只需要枚举列表而不修改它,请查看Eric Lippert在IEnumerable<Animal>
上的答案。
您正在寻找通用收集协方差。 显然,这个function不支持你正在使用的C#版本。
您可以使用Cast<T>()
扩展方法解决此问题。 但请注意,这将创build您的原始列表的副本,而不是将原始格式作为其他types传递:
cat.Play((new List<Cat>()).Cast<Animal>().ToList());
使用扩展方法Cast()
所以:
class Program { static void Main(string[] args) { Cat cat = new Cat(); cat.Play(new List<Cat>().Cast<Animal>()); } }
这是因为B / C .net 3.5不支持协方差,但4.0呢:)
每个人都已经提到了施放方法。 如果你不能更新到4.0隐藏的演员的方式是
class Cat : Animal { public override void Play(List<Animal> animal) { Play((List<Cat>)animal); } public virtual void Play(List<Cat> animal) { } }
这与GetEnumerator的IEnumable
和IEnumarable<T>
玩法是一样的