繁体   English   中英

将派生类的 List<> 转换为基类的 List<>

[英]Casting List<> of Derived class to List<> of base class

我有两个类:一个基类(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>());
        }
    }
}

当我编译上述程序时,我收到以下错误

Error    2    Argument 1: cannot convert from 'System.Collections.Generic.List' to '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!

好吧,狗我的猫,那是坏事。

您想要的功能称为“通用协方差”,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泛型,而是使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) 
    {
    }
}

最后,如果您使用 C# 4 并且只需要枚举列表而不修改它,请查看 Eric Lippert 在IEnumerable<Animal>上的回答。

您正在寻找通用集合协方差。 但显然,您使用的 C# 版本不支持该功能。

您可以使用Cast<T>()扩展方法来解决它。 但是请注意,这将创建原始列表的副本,而不是将原始列表作为不同的类型传递:

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 支持:)

每个人都已经提到了 cast 方法。 如果你不能更新到 4.0 隐藏演员的方法是

class Cat : Animal
{
    public override void Play(List<Animal> animal)
    {
         Play((List<Cat>)animal);
    }
    public virtual void Play(List<Cat> animal)
    {
    }
}

这与IEnumableIEnumarable<T>为 GetEnumerator 玩的技巧相同

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM