2010-09-15 2 views
22

У меня есть два класса: базовый класс (Animal) и класс, полученный из it (Cat). Базовый класс содержит один виртуальный метод Play, который принимает List as вход parameter.Something как этотСписок кастингов <> из класса Derived в список <> базового класса

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' 

есть в любом случае для достижения этой цели?

+3

возможно дубликат [Бросая общий сбор для базового типа] (HTTP: //stackoverflow.com/questions/539287/casting-a-generic-collection-to-base-type) – finnw

+1

Первое, что нужно сделать, это изменить аргумент List <> на аргумент IEnumerable <>. –

+0

Возможный дубликат [Преобразование списка <> объектов производного класса в Список <> объектов базового класса] (http://stackoverflow.com/questions/1817300/convert-list-of-derived-class-objects-to-list -of-base-class-objects) –

ответ

42

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

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

+0

Мне не хватает того, как игра, являющаяся членом Animal, добавляет к объяснению. То есть, игра выполняется с использованием IEnumerable . IMO было бы более ясным, если бы это был Play(), и пример был помещен в void Main() с использованием назначения. – Dykam

+1

@ Dykam: Это не так. Я пытался следовать структуре кода, изложенной в оригинальном плакате. –

+0

А, я вижу. По какой-то причине я не связывал два фрагмента. – Dykam

10

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

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

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

Он не поддерживается списком вообще, поскольку он является in & out generic. – Dykam

+0

@ Dykam - Вы можете ласково прокомментировать? Комментарий не имеет большого смысла, как есть. –

+0

Ну, так как List принимает входные данные, также возвращает вывод, co- и contravariance невозможен. Если вы сможете сделать «Список list = new List ();' Следующее приведет к сбою приложения: 'list.Add (new Elephant());'. – Dykam

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 делает :)

+0

Причина отключена. 'cat.Play (новый список ());' также не будет работать в 4 с существующим кодом. –

13

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

+0

+1 для общей трюки –

+0

Также +1 для указания, что вы, вероятно, должны использовать IEnumerable для типа параметра, а не List –

+0

Я не понимаю, что последний бит; почему класс Cat имеет метод с параметром типа Cat? Разве это не слишком запутанно, иметь два типа с одним и тем же именем внутри одной и той же декларации? –

2

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

Смежные вопросы