2009-03-04 46 views
6

Gần đây tôi đã tìm thấy một hành vi rất đáng ngạc nhiên trong C#. Tôi đã có một phương pháp mất IEnumerable<Object> làm thông số và tôi đã chuyển số IEnumerable<string> nhưng không thể. Trong khi trong C# mọi thứ có thể được upcast để đối tượng hơn lý do tại sao điều này là không thể? Nó hoàn toàn gây nhầm lẫn cho tôi. Xin vui lòng ai đó làm rõ tôi về vấn đề này.Truyền từ IEnumerable <Object> tới IEnumerable <string>

+0

Làm thế nào là không thể? Nó ném một ArgumentException? Bên cạnh đó, điểm của việc sử dụng một IEnumerable chung là gì nếu bạn định khai báo nó như một đối tượng? –

+0

Đây là lỗi biên dịch –

+0

Giống như: http://stackoverflow.com/questions/6557/in-c-why-cant-a-liststring-object-be-stored-in-a-listobject-variable –

Trả lời

11

Thuật ngữ kỹ thuật cho điều này là generics là bất biến trong C# 3.0 và trước đó. Từ C# 4.0 trở đi, dàn diễn viên hoạt động.

Phương tiện bất biến là không có mối quan hệ giữa hai loại chung chung chỉ vì kiểu chung của chúng thông số có liên quan (tức là phụ hoặc siêu mẫu của nhau).

Trong ví dụ của bạn, không có mối quan hệ nhập giữa IEnumerable<object>IEnumerable<string>, chỉ vì chuỗi là một loại phụ của đối tượng. Chúng chỉ được coi là hai loại hoàn toàn không liên quan, giống như một chuỗi và một int (chúng vẫn là hai kiểu con của đối tượng, nhưng mọi thứ đều là)

Có một vài cách giải quyết và ngoại lệ cho vấn đề này mà bạn đã gặp phải.

Trước tiên, bạn có thể truyền từng chuỗi riêng lẻ thành đối tượng, nếu bạn đang sử dụng .NET 3.0, bạn có thể thực hiện điều đó bằng phương pháp mở rộng Cast<T>(). Nếu không, bạn có thể sử dụng foreach và đưa kết quả vào một biến mới của kiểu tĩnh mà bạn muốn.

Thứ hai, mảng là ngoại lệ cho loại tham chiếu, tức là chuyển loại chuỗi [] sang loại đối tượng nhận dạng đối tượng [] sẽ hoạt động.

+1

Chỉ cần cập nhật câu trả lời cũ, hiệp phương sai và contravariance này hiện được hỗ trợ trong C#, và bây giờ đây là một diễn viên hợp lệ. –

0

Bạn nên sử dụng

IEnumerable<T> 

nếu bạn muốn vượt qua trong các loại khác nhau, sau đó bạn có thể truy vấn T để tìm hiểu những gì nó là loại.

0

Một cách hay để suy nghĩ về điều này là tự hỏi "Điều gì sẽ xảy ra nếu bạn có thể làm điều này?". Lấy ví dụ sau:

IEnumerable<String> strings=...; 
IEnumerable<Object> objects = strings; // assume this were legal 

objects.Add(new Integer(5)); // what the... 

Chúng tôi vừa thêm một số nguyên vào danh sách các chuỗi. Trình biên dịch thực hiện điều này để bảo vệ an toàn kiểu.

+0

IEnumerable không có phương thức Thêm mặc dù ... Và trong C# 4.0, bạn sẽ thực sự có thể thực hiện thao tác này vì tuyên bố sẽ thay đổi thành IEnumerable cho phép nó được (an toàn) contravariant. –

+0

Cũng thay thế bất kỳ lớp chung nào có phương thức Thêm như Danh sách thì –

+0

-1: 'IEnumerable ' trở thành 'IEnumerable 'trong .NET 4 đặc biệt vì đối số của bạn có ý nghĩa đối với vùng chứa nhưng * không * cho một liệt kê. –

3

Cách dễ nhất để vượt qua IEnumerable<string> hoạt động đòi hỏi IEnumerable<object> là thông qua chuyển đổi chức năng như thế này:

public IEnumerable<object> convert<T>(IEnumerable<T> enumerable) 
    { 
    foreach (T o in enumerable) 
     yield return o; 
    } 

Khi C# 4 đi ra, điều này sẽ không cần thiết, bởi vì nó sẽ hỗ trợ hiệp phương sai và contravariance.

+2

Đây chính là điều Cast <> là dành cho :) –

+0

Cảm ơn! Tôi đã upvoted câu trả lời này đề nghị này. –

8

Như những người khác đã chỉ ra, các loại generics là bất biến. IEnumerable<T> có thể là đồng biến thể nhưng C# hiện không hỗ trợ chỉ định các biến thể. C# 4.0 dự kiến ​​sẽ hỗ trợ các biến thể để điều này có thể được hỗ trợ trong tương lai.

Để làm việc này, bạn có thể sử dụng phương pháp mở rộng LINQ Cast<object>(). Giả sử bạn có một phương thức có tên là Foo có một số IEnumerable<object>>.Bạn có thể gọi nó như thế này,

Foo(stringEnumerable.Cast<object>()); 
Các vấn đề liên quan