投射列表<>列出<>的派生类基类

时间:2010-09-15 18:47:24

标签: c# generics

我有两个类:基类(Animal)和派生自的类 它(Cat).Base类包含一个虚拟方法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'

无论如何要做到这一点?

5 个答案:

答案 0 :(得分:49)

您无法执行此操作的原因是因为列表是可写的。假设它是合法的,看看出了什么问题:

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>将狗添加到实际上是猫列表的内容中。

答案 1 :(得分:14)

你可以做一些事情。一个示例是将列表的元素转换为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>上的答案。

答案 2 :(得分:10)

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

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

cat.Play((new List<Cat>()).Cast<Animal>().ToList());

答案 3 :(得分:3)

使用扩展方法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 :(得分:2)

每个人都提到了施法方法。如果你无法更新到4.0隐藏演员阵容的方法是

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

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