2011-12-19 51 views
7

thể trùng lặp:
In C#, why can't a List<string> object be stored in a List<object> variableTại sao tôi không thể nhập từ điển của một loại giá trị vào từ điển của một loại giá trị khác khi các loại giá trị có thể được truyền từ một loại giá trị khác?

Tại sao không phải là công việc dưới đây?

List<string> castMe = new List<string>(); 
IEnumerable<string> getFromCast = (IEnumerable<string>)castMe; // allowed. 

Dictionary<int, List<string>> castMeDict = new Dictionary<int, List<string>>(); 
Dictionary<int, IEnumerable<string>> getFromDict = (Dictionary<int, IEnumerable<string>>)castMeDict; // Not allowed 

Đây có phải là lỗ hổng trong cơ chế đúc Dictionary hoặc trong suy nghĩ của tôi rằng điều này nên được cho phép không?

Cảm ơn.

+5

Xem: [Hiểu hiệp biến và contravariant giao diện trong C# ] (http://stackoverflow.com/questions/2719954/understanding-covariant-and-contravariant-interfaces-in-c-sharp) – Magnus

+0

Cảm ơn mọi người. Xin lỗi, tôi đã kiểm tra bản sao nhưng có một chút khó khăn khi khái quát trước, sau đó tìm kiếm tất cả các phiên bản cụ thể có thể trùng lặp mà không biết câu trả lời. – user420667

Trả lời

20

Đây có phải là lỗ hổng trong cơ chế truyền từ điển hay trong suy nghĩ của tôi rằng điều này nên được cho phép?

Trong suy nghĩ của bạn. Bạn đang mong rằng các từ điển phải là covariant trong các chuyển đổi của chúng. Họ không, vì lý do sau đây. Giả sử họ, và suy ra những gì có thể đi sai:

Dictionary<int, List<string>> castMeDict = 
    new Dictionary<int, List<string>>(); 

Dictionary<int, IEnumerable<string>> getFromDict = 
    (Dictionary<int, IEnumerable<string>>)castMeDict; 

castMeDict[123] = new List<string>(); 
IEnumerable<string> strings = getFromDict[123]; // No problem! 
getFromDict[123] = new string[] { "hello" }; // Big problem! 

Một mảng của chuỗi là mui trần để IEnumerable<string> nhưng không List<string>. Bạn chỉ cần đặt một cái gì đó là không một danh sách chuỗi vào từ điển chỉ có thể lấy danh sách chuỗi.

Trong C# loại generic có thể hiệp biến hoặc contravariant nếu tất cả các điều kiện sau đây được đáp ứng:

  • Bạn đang sử dụng C# 4 trở lên.
  • Loại chung chung khác nhau là giao diện hoặc đại biểu.
  • Phương sai được chứng minh là an toàn. (Thông số C# mô tả các quy tắc chúng tôi sử dụng để xác định độ an toàn phương sai. C# 4.0 Phiên bản tệp doc có thể được tải xuống [here]. Xem phần 23.5.)
  • Các đối số kiểu khác nhau là tất cả các loại tham chiếu.
  • Loại đã được đánh dấu cụ thể là an toàn cho phương sai.

Hầu hết các điều kiện này không được đáp ứng cho từ điển - nó không phải là giao diện hoặc đại biểu, không an toàn, và loại không được đánh dấu là an toàn cho phương sai. Vì vậy, không có phương sai cho từ điển.

IEnumerable<T> ngược lại không đáp ứng tất cả các điều kiện đó. Bạn có thể chuyển đổi IEnumerable<string> để IEnumerable<object> trong C# 4.

Nếu đối tượng phương sai bạn quan tâm, xem xét đọc hai chục bài viết của tôi về đề tài này:

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

+0

Tôi hiểu. Vì vậy, vấn đề là castMeDict vẫn còn xung quanh mặc dù tôi chỉ sử dụng nó để xây dựng từ điển ở nơi đầu tiên. – user420667

+2

@ user420667: Đúng. Bây giờ, nếu có một cách để nói "Tôi hứa rằng tôi sẽ chỉ bao giờ * đọc * từ từ điển này sau khi chuyển đổi nó, và hơn nữa, tôi sẽ chỉ đọc * từ bất kỳ đối tượng nào tôi đọc bằng từ điển, và cứ thế "thì chúng ta có thể biến nó thành an toàn. Nhưng không có cách nào để thể hiện lời hứa đó trong hệ thống kiểu CLR, và không có cách nào để thực thi nó, ngoài việc * làm cho từ điển hỗ trợ giao diện chỉ đọc được đảm bảo an toàn *. Đó là lý do tại sao bạn có thể coi một 'Danh sách 'là một' IEnumerable ' - bởi vì không có cách nào cho 'IE ' thành * ghi *. –

+0

Hm. Tôi đoán nếu tôi sẽ chỉ sử dụng các đối tượng để đọc thì nó không thực sự là một từ điển nhưng một lớp học đặc biệt giống như từ điển hạn chế những người định cư. Một chút giống như những gì Jon Hanna đã gợi ý. – user420667

4

Nghịch lý nghiên cứu và hiệp phương sai. Đối với một ví dụ cụ thể về lý do tại sao một trường hợp như vậy có thể là một điều xấu, hãy kiểm tra this answer by Jon Skeet.

2

Hãy suy nghĩ về những gì sẽ xảy ra nếu nó được cho phép và sau đó bạn làm:

getFromDict.Add(34, new HashSet<string>); 

Đó là cho phép một cách hoàn hảo; HashSet<string> thực hiện IEnumerable<string> và có thể được thêm làm giá trị cho một Dictionary<int, IEnumerable<string>>. Nó không thể mặc dù được thêm vào một Dictionary<int, List<string>>, đó là những gì mà đối tượng thực sự là.

Nếu bạn muốn sử dụng nó làm chỉ đọc IDictionary<int, IEnumerable<string>> thì bạn có thể nhận được hiệu quả tốt từ lớp trình bao bọc phôi thành IEnumerable<string> khi nó hoạt động.

Nếu không, bạn cần phải sao chép các giá trị vào một Dictionary<int, IEnumerable<string>> mới.

+0

thực sự đây là chính xác những gì tôi cần cho hiệu quả, cảm ơn. – user420667

+0

Ý tưởng bao bọc? Bạn có rõ ràng về cách để đi về điều đó? –

+0

Tôi sẽ tưởng tượng nó giống như bạn tạo ra một lớp thực hiện IDictionary > lấy từ điển > làm một hàm tạo, giữ nó như một thành viên, và ghi đè [] và nhận các toán tử. Đó có phải là những gì bạn có trong đầu không? Cảm ơn. – user420667

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