2010-09-15 37 views
22

Tôi có hai lớp: một lớp cơ sở (Động vật) và một lớp bắt nguồn từ nó (Cat). Lớp lớp chứa một phương thức ảo đầu vào parameter.Something như thế nàyDanh sách đúc <> của lớp bắt nguồn vào Danh sách <> của lớp cơ sở

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>()); 
     } 
    } 
} 

Khi tôi biên dịch chương trình trên, tôi nhận được lỗi sau

 
    Error 2 Argument 1: cannot convert from 'System.Collections.Generic.List' to 'System.Collections.Generic.List' 

Liệu có cách nào để thực hiện điều này?

+3

thể trùng lặp của [Đúc một bộ sưu tập tổng quát để loại cơ sở] (http: //stackoverflow.com/questions/539287/casting-a-generic-collection-to-base-type) – finnw

+1

Điều đầu tiên cần làm là thay đổi đối số List <> thành đối số IEnumerable <>. –

+0

có thể trùng lặp của [Danh sách chuyển đổi <> của các đối tượng lớp dẫn xuất thành Danh sách <> của các đối tượng lớp cơ sở] (http://stackoverflow.com/questions/1817300/convert-list-of-derived-class-objects-to-list -of-base-class-objects) –

Trả lời

42

Lý do bạn không thể làm điều này là do danh sách có thể ghi được. Giả sử nó là hợp pháp và xem điều gì sai:

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! 

Vâng chó mèo của tôi, điều đó thật tệ.

Tính năng bạn muốn được gọi là "hiệp phương sai chung" và được hỗ trợ trong C# 4 cho các giao diện được biết là an toàn. IEnumerable<T> không có cách nào để ghi vào trình tự, vì vậy nó an toàn.

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>());  
    }  
} 

Điều đó sẽ làm việc trong C# 4 vì List<Cat> là mui trần để IEnumerable<Cat>, đó là chuyển đổi thành IEnumerable<Animal>. Không có cách nào mà Play có thể sử dụng IEnumerable<Animal> để thêm một chú chó vào thứ gì đó thực sự là danh sách các con mèo.

+0

Tôi đang bỏ lỡ cách Play là thành viên của Animal thêm vào phần giải thích. Tức là, Play đang sử dụng số của IEnumerable. IMO nó sẽ rõ ràng hơn nếu nó chỉ là Play() và ví dụ được đặt trong void Main() bằng cách sử dụng gán. – Dykam

+1

@Dykam: Nó không. Tôi đã cố gắng để làm theo các cấu trúc mã đặt ra bởi các poster ban đầu. –

+0

Ah, tôi hiểu rồi. Vì một lý do nào đó tôi không liên kết hai đoạn. – Dykam

10

Bạn đang tìm phương sai hiệp phương thu chung. Rõ ràng, mặc dù, tính năng đó không được hỗ trợ bởi phiên bản C# mà bạn đang sử dụng.

Bạn có thể làm việc xung quanh nó bằng cách sử dụng phương pháp mở rộng Cast<T>(). Hãy nhận biết, tuy nhiên, điều này sẽ tạo ra một bản sao của danh sách ban đầu của bạn thay vì đi qua các gốc như một loại khác nhau:

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

Nó không được hỗ trợ bởi Danh sách, vì nó là một trong và ngoài chung chung. – Dykam

+0

@Dykam - Bạn có thể làm tròn không? Nhận xét không có ý nghĩa nhiều như vậy. –

+0

Vâng, như danh sách cả hai đầu vào, cũng như trả về đầu ra, đồng và contravariance là không thể. Nếu bạn có thể thực hiện 'List list = new List ();' Sau đây sẽ làm hỏng ứng dụng: 'list.Add (new Elephant());'. – Dykam

3

sử dụng Cast phương pháp khuyến nông()

vậy:

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

Lý do cho điều này là b/c .net 3.5 không hỗ trợ hiệp phương sai, nhưng 4.0 không :)

+0

Lý do bị tắt. 'cat.Play (danh sách mới ());' cũng sẽ không hoạt động trong 4 với mã hiện có. –

13

Bạn có thể làm một vài việc. Một ví dụ được cast yếu tố của danh sách để Animal

Sử dụng mã của bạn:

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

khác là để làm cho Animal chung chung, do cat.Play(new List<Cat>()); sẽ làm việc.

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>()); 
    } 
} 

Một phương pháp khác là không làm cho Animal chung chung, nhưng Play phương pháp và hạn chế đó để 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) 
    { 
    } 
} 

Cuối cùng, nếu bạn đang ở trên C# 4 và chỉ cần liệt kê trên danh sách và không sửa đổi nó, hãy kiểm tra câu trả lời của Eric Lippert trên IEnumerable<Animal>.

+0

+1 cho lừa chung –

+0

Ngoài ra +1 để chỉ ra rằng bạn có lẽ nên sử dụng IEnumerable cho kiểu tham số thay vì Danh sách –

+0

Tôi không hiểu rằng bit cuối cùng; tại sao lớp Cat có một phương thức với một tham số kiểu có tên là Cat? Không phải là điều cực kỳ khó hiểu, có hai loại có cùng tên trong cùng một tuyên bố? –

2

Mọi người đều đề cập đến phương pháp truyền. Nếu bạn không thể nâng cấp lên 4.0 một cách để che giấu các diễn viên là

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

Đây là giống lừa IEnumableIEnumarable<T> chơi cho GetEnumerator

Các vấn đề liên quan