2009-02-25 31 views
5

Tôi có một thói quenC# .NET đi qua một bộ sưu tập của các đối tượng InterfaceImplementingClass để một thói quen mà phải mất một tập hợp các giao diện đối tượng

public void SomeRoutine(List<IFormattable> list) { ... } 

sau đó tôi cố gắng gọi thói quen

List<Guid>list = new List<Guid>(); 
list.Add(Guid.NewGuid()); 
SomeRoutine(list); 

này Và nó không thành công với một lỗi biên dịch thời gian. System.Guid thực hiện IFormattable, nhưng lỗi tôi nhận được là

không thể chuyển đổi từ 'System.Collections.Generic.List' để 'System.Collections.Generic.List'

LƯU Ý : Bạn sẽ nhận được cùng một lỗi nếu bạn chỉ sử dụng một loạt các hướng dẫn. Generics KHÔNG phải là nguyên nhân ....

Nhưng! Với này

public void SomeRoutine2(IFormattable obj) { ... } 

và điều này

Guid a = Guid.NewGuid(); 
SomeRoutine2(a); 

Nó biên dịch! Vì vậy, câu hỏi là TẠI SAO? Tại sao tôi có thể truyền một đối tượng Guid (thực hiện IFormattable) thành một thường trình chấp nhận một đối tượng IFormattable, nhưng khi tôi cố gắng mở rộng một bộ sưu tập (một danh sách chung hoặc một mảng hoặc bất kỳ thứ gì khác), tôi nhận được một lỗi chuyển đổi?

Tôi đã có một thời gian tìm kiếm câu trả lời và tôi đã nhận ra đây là nơi tốt nhất để đi.

+0

Bạn nhận ra rằng câu trả lời được đánh dấu (.NET 4.0 covariance) không thực sự áp dụng cho các danh sách, nơi mà như generics hoạt động * bây giờ *? –

+0

Xem thêm: http://marcgravell.blogspot.com/2009/02/what-c-40-covariance-doesn-do.html –

+0

Ngoại trừ việc nó không hoạt động với một loạt các Hướng dẫn có thói quen đang tìm kiếm một mảng của IFormattable, hoặc là ....? – emkayultra

Trả lời

8
+0

C# 4.0 hiệp phương sai không áp dụng cho các lớp cụ thể (chỉ cho giao diện), và không áp dụng cho các danh sách (chỉ IEnumerable - hoặc những thứ được tinh khiết "ra") –

+0

Bạn có ý nghĩa gì khi bạn nói "tinh khiết ra "? – BFree

+0

Ý tôi là, 'IEnumerable 'và' IEnumerator ' chỉ có "sử dụng" - nghĩa là "T Current {get;}". Nó đánh dấu những thứ như "in" hoặc "out" cho phép co/contra-variance; nhưng nó chỉ hoạt động với một ** hoặc ** khác. Bạn không thể sử dụng "in" và "out" (danh sách nào sẽ yêu cầu). –

2

Bạn đã cố gắng tuyên bố list như một đối tượng List<IFormattable> thay vì một List<Guid>? Điều đó sẽ giúp bạn vượt qua lỗi biên dịch.

+0

Generics là một lựa chọn tuyệt vời hơn ... –

3

Vấn đề là một Danh sách <IFormattable> không chỉ là một bộ sưu tập mà từ đó bạn có thể đọc một số IFormattables, nó cũng là một bộ sưu tập mà bạn có thể thêm IFormattables. Danh sách < Hướng dẫn > đáp ứng yêu cầu đầu tiên, nhưng không đáp ứng yêu cầu thứ hai. Điều gì nếu SomeRoutine là

public void SomeRoutine(List<IFormattable> list) 
{ 
    list.Add(5); 
} 

Đó Int32 là một IFormattable, vì vậy phương pháp này sẽ có thể thêm nó vào danh sách <IFormattable> mà nó yêu cầu. Điều đó sẽ không hoạt động nếu trình biên dịch cho phép bạn chuyển vào Danh sách < Hướng dẫn > của bạn.

Tính năng C# 4.0 mới mà BFree đề cập đến sẽ cho phép bạn thông báo cho trình biên dịch khi những điều này an toàn.Bạn có thể nói hoặc là

  • Mặc dù tôi đã yêu cầu tham chiếu IFormattable, tôi sẽ chỉ đọc nó. Nếu nó thực sự là một tài liệu tham khảo Guid thì tôi có thể xử lý nó một cách an toàn như một IFormattable, và tôi sẽ không cố gắng gán một Int32 cho nó.
  • Mặc dù tôi đã yêu cầu một tham chiếu IFormattable, tôi sẽ chỉ viết thư cho nó. Nếu nó thực sự là một tham chiếu Object thì tôi có thể gán IFormattable một cách an toàn cho nó, và nó không quan trọng nếu giá trị hiện tại không phải là một IFormattable vì tôi sẽ không đọc nó.
+0

C# 4.0 hiệp phương sai doesn ' t áp dụng cho các lớp cụ thể (chỉ cho các giao diện), và không áp dụng cho các danh sách (chỉ IEnumerable - hoặc những thứ được tinh khiết "ra" –

+0

sửa; giao diện + đại biểu, nhưng generics vẫn là một câu trả lời tốt hơn ở đây ... –

+0

Tất nhiên, một danh sách không thể thực hiện một trong các xác nhận 'in' hoặc 'out' mà C# 4.0 sẽ cho phép bởi vì bạn vẫn cần phải có khả năng chèn và lấy T's, vì vậy bạn không có gì tốt hơn. 1 cho generics của bạn trả lời. – stevemegson

5

Đây là trường hợp sử dụng Generics cổ điển; thử:

public static void SomeRoutine<T>(IList<T> list) where T : IFormattable 
{ ... } 

Bây giờ bên SomeRoutine bạn có quyền truy cập vào tất cả các IFormattable thành viên, nhưng nó sẽ làm việc với:

List<Guid>list; ... 
SomeRoutine(list); // note no need to specify T 

Edit: Tôi đã bổ sung blogged về sự khác biệt giữa 4,0 hiệp phương sai và Generics cho kịch bản này.

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