代码之家  ›  专栏  ›  技术社区  ›  santosh singh

将派生类的列表转换为基类的列表

  •  23
  • santosh singh  · 技术社区  · 14 年前

    基类包含一个以List作为输入的虚拟方法Play参数什么的这样地

    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 回复  |  直到 14 年前
        1
  •  54
  •   Eric Lippert    14 年前

    不能这样做的原因是列表是可写的。假设这是合法的,看看出了什么问题:

    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> . 这场比赛没有办法用 IEnumerable<动物>

        2
  •  15
  •   Anthony Pegram    14 年前

    你可以做一些事情。一个例子是 元素 列表的 Animal

    使用您的代码:

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

    另一个是制造 一般的,所以 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>());
        }
    }
    

    另一种方法是不要 通用,但 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>

        3
  •  12
  •   Justin Niessner    14 年前

    你在寻找通用集合协方差。不过,很明显,您使用的C#版本不支持该功能。

    您可以使用 Cast<T>()

    cat.Play((new List<Cat>()).Cast<Animal>().ToList());
    
        4
  •  3
  •   Jose    14 年前

    所以:

    class Program
    {
        static void Main(string[] args)
        {
            Cat cat = new Cat();
            cat.Play(new List<Cat>().Cast<Animal>());
        }
    }
    

        5
  •  2
  •   Scott Chamberlain    14 年前

    每个人都提到了铸造方法。如果无法更新到4.0,则隐藏强制转换的方法是

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

    这是同样的把戏 IEnumable IEnumarable<T> 为GetEnumerator播放