Список приведения <> производного класса к списку <> базового класса - PullRequest
23 голосов
/ 15 сентября 2010

У меня есть два класса: базовый класс (Animal) и класс, производный от Класс it (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 ]

49 голосов
/ 15 сентября 2010

Причина, по которой вы не можете этого сделать, заключается в том, что список доступен для записи.Предположим, это было законно, и посмотрим, что пошло не так:

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>, чтобы добавить собаку к чему-то, что фактически является списком кошек.

14 голосов
/ 15 сентября 2010

Вы могли бы сделать несколько вещей.Один пример - приведение элементов списка к 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 и вам нужно только перечислить список и не изменять его, проверьте ответ Эрика Липперта по IEnumerable<Animal>.

11 голосов
/ 15 сентября 2010

Вы ищете общую ковариацию коллекции.Однако очевидно, что эта функция не поддерживается используемой версией C #.

Вы можете обойти ее, используя метод расширения Cast<T>().Имейте в виду, однако, что это создаст копию вашего оригинального списка вместо передачи оригинала как другого типа:

cat.Play((new List<Cat>()).Cast<Animal>().ToList());
3 голосов
/ 15 сентября 2010

использовать метод расширения 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 поддерживает:)

2 голосов
/ 15 сентября 2010

Каждый уже упоминает метод приведения.Если вы не можете обновить до 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

.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...