2010-10-27 36 views
18

Có thể đúc List<Subclass> đến List<Superclass> trong C# 4.0 không?hiệp phương sai trong C#

cái gì đó dọc những dòng này:

class joe : human {} 

List<joe> joes = GetJoes(); 

List<human> humanJoes = joes; 

Đó không phải là những gì hiệp phương sai là gì?

nếu bạn có thể làm:

human h = joe1 as human; 

lý do tại sao bạn không nên có thể làm

List<human> humans = joes as List<human>; 

hơn nó sẽ không được pháp luật để làm (joe) con người [0] vì đó mục đã được đúc xuống .. và tất cả mọi người sẽ được hạnh phúc. Bây giờ, cách duy nhất là tạo một Danh sách mới

+2

Đây là cơ bản giống như [Trong C#, tại sao có thể không phải là một Danh sách đối tượng được lưu trữ trong một biến Danh sách ] (http://stackoverflow.com/questions/6557/in-c-why-cant-a-liststring-đối tượng-được-lưu-trong-a-danh sách-biến-biến). –

+0

vì 'con người' sau đó sẽ đề cập đến một thể hiện của' Danh sách ', điều này sẽ gây ra các vấn đề như minh họa trong ví dụ của @ Jon. –

+0

yep, sau khi chỉnh sửa ví dụ tôi đã hiểu nó .. có ý nghĩa –

Trả lời

25

Bạn không thể làm điều này, bởi vì nó sẽ không được an toàn. Xem xét:

List<Joe> joes = GetJoes();  
List<Human> humanJoes = joes; 
humanJoes.Clear(); 
humanJoes.Add(new Fred()); 
Joe joe = joes[0]; 

Rõ ràng dòng cuối cùng (nếu không phải là một sớm một) thất bại - như một Fred không phải là một Joe. Sự bất biến của List<T> ngăn chặn lỗi này tại biên dịch thay vì thời gian thực hiện.

+0

cũng humanJoes là một danh sách của con người, vì vậy bạn không nên cố gắng (hoặc có thể) để thêm một Joe từ nó. nhưng mỗi joe là một con người, vì vậy nó sẽ an toàn để đúc tất cả các phân lớp một cách an toàn. –

+0

bạn chắc chắn có thể làm Human h = joesList [0] như Human .. và đó là những gì tôi đang tìm kiếm/hỏi về .. chấp nhận trên một danh sách toàn bộ cấp độ –

+0

@Sonic: Tại sao bạn không thêm một 'Joe' hoặc 'Fred' với nó? * Điều đó * loại phương sai là * luôn luôn * chấp nhận được. Hãy thử: 'Danh sách danh sách = danh sách mới (); list.Add ("đây là một chuỗi)"; '. Tại sao bạn mong đợi rằng để thất bại? –

2

Không. Các tính năng đồng/contravariance của C# 4.0 chỉ hỗ trợ giao diện và đại biểu. Không hỗ trợ các loại bê tông như List<T>.

+0

http://stackoverflow.com/questions/245607/how-is-generic-covariance-contra-variance-implemented-in-c-4-0 –

+0

vì vậy tôi cần phải lặp lại và tạo ra một đối tượng mới cho mỗi joe như con người và thêm vào danh sách mới? đó là lựa chọn duy nhất? –

+1

Không thể thực hiện với 'IList ', bởi vì đó là bất biến. –

6

Khởi tạo một cá nhân-danh sách mới mà mất Joes như là đầu vào:

List<human> humanJoes = new List<human>(joes); 
1

Không. Như Jared đã nói, các tính năng co/contravariance của C# 4.0 chỉ hỗ trợ giao diện và đại biểu. Tuy nhiên, nó cũng không hoạt động với IList<T>, và lý do là IList<T> chứa các phương thức để thêm và thay đổi các mục trong danh sách - như câu trả lời mới của Jon Skeet nói.

Cách duy nhất để có thể đúc một danh sách các "joe" thành "con người" là nếu giao diện là chỉ đọc hoàn toàn theo thiết kế, một cái gì đó như thế này:

public interface IListReader<out T> : IEnumerable<T> 
{ 
    T this[int index] { get; } 
    int Count { get; } 
} 

Ngay cả một phương pháp Contains(T item) sẽ không được phép, bởi vì khi bạn truyền IListReader<joe> đến IListReader<human>, không có phương thức Contains(human item) trong IListReader<joe>.

Bạn có thể "ép" dàn diễn viên từ IList<joe> đến IListReader<joe>, IListReader<human> hoặc thậm chí IList<human> bằng cách sử dụng GoInterface. Nhưng nếu danh sách đủ nhỏ để sao chép, một giải pháp đơn giản hơn là chỉ cần sao chép nó vào một List<human> mới, như Paw đã chỉ ra.

0

Nếu tôi cho phép List<Joe> joes của bạn để được khái quát như ...

List<Human> humans = joes; 

... hai tài liệu tham khảo humansjoes là, bây giờ trở đi, trỏ đến cùng một danh sách chính xác. Mã theo phân công ở trên không có cách nào để ngăn chặn việc chèn/thêm một thể hiện của một loại người khác, nói một Thợ sửa ống nước, vào danh sách.Cho rằng class Plumber: Human {}

humans.Add(new Plumber()); // Add() now accepts any Human not just a Joe 

danh sách mà humans đề cập đến bây giờ chứa cả Joes và thợ sửa ống nước. Lưu ý rằng cùng một đối tượng danh sách vẫn được tham chiếu bởi tham chiếu joes. Bây giờ nếu tôi sử dụng tham chiếu joes để đọc từ đối tượng danh sách, tôi có thể bật ra một thợ ống nước thay vì một joe. Thợ sửa ống nước và Joe không được biết đến là hoàn toàn có thể xen kẽ ... vì vậy tôi nhận được một thợ sửa ống nước thay vì một joe từ danh sách phá vỡ an toàn loại. Một thợ ống nước chắc chắn không được chào đón thông qua một tham chiếu đến một danh sách các tai họa. Tuy nhiên, trong các phiên bản gần đây của C#, loại của nó có thể làm việc xung quanh giới hạn này cho một lớp chung/bộ sưu tập bằng cách thực hiện một giao diện chung có tham số kiểu có một sửa đổi out trên nó. Giả sử bây giờ chúng tôi có ABag<T> : ICovariable<out T>. Công cụ sửa đổi ngoài chỉ giới hạn T thành vị trí đầu ra (ví dụ: các kiểu trả về của phương thức). Bạn không thể nhập bất kỳ T vào túi. Bạn chỉ có thể đọc chúng ra khỏi nó. Điều này cho phép chúng ta khái quát hóa joes thành một ICovariable<Human> mà không phải lo lắng về việc chèn một Thợ sửa ống nước vào nó vì giao diện không cho phép điều đó. Bây giờ chúng ta có thể viết ...

ICovariable<Human> humans = joes ; // now its good ! 
humans.Add(new Plumber()); // error 
Các vấn đề liên quan