2010-05-06 10 views
36

Trong đoạn mã sau tôi mong đợi có thể truyền hoàn toàn từ elements đến baseElementsTBase được chuyển đổi hoàn toàn thành IBase.Đây có phải là lỗi hiệp phương sai trong C# 4 không?

public interface IBase { } 
public interface IDerived : IBase { } 
public class VarianceBug 
{ 
    public void Foo<TBase>() where TBase : IBase 
    { 
     IEnumerable<TBase> elements = null; 
     IEnumerable<IDerived> derivedElements = null; 
     IEnumerable<IBase> baseElements; 

     // works fine 
     baseElements = derivedElements; 

     // error CS0266: Cannot implicitly convert type 
     // 'System.Collections.Generic.IEnumerable<TBase>' to 
     // 'System.Collections.Generic.IEnumerable<IBase>'. 
     // An explicit conversion exists (are you missing a cast?) 
     baseElements = elements; 
    } 
} 

Tuy nhiên, tôi nhận được lỗi được đề cập trong nhận xét.

Trích dẫn từ spec:

Một loại T<A1, …, An> là sai-mui trần cho một loại T<B1, …, Bn> nếu T hoặc là một giao diện hoặc một loại đại biểu tuyên bố với các thông số loại biến thể T<X1, …, Xn>, và đối với mỗi tham số kiểu biến thể Xi một trong những điều sau đây giữ:

  • Xi là hiệp biến và một tham chiếu ngầm hoặc chuyển đổi danh tính tồn tại từ Ai để Bi

  • Xi là contravariant và một tham chiếu ngầm hoặc chuyển đổi danh tính tồn tại từ Bi để Ai

  • Xi là bất biến và chuyển đổi danh tính tồn tại từ Ai để Bi

Kiểm tra mã của tôi, nó có vẻ phù hợp với thông số:

  • IEnumerable<out T> là một kiểu giao diện

  • IEnumerable<out T> được khai báo với các thông số loại biến thể

  • T là hiệp biến

  • một chuyển đổi tài liệu tham khảo ngầm tồn tại TBase-IBase

Vậy - có phải là lỗi trong trình biên dịch C# 4 không?

+0

gì xảy ra khi bạn cast một cách rõ ràng? Trình biên dịch nói rằng có một. Vì bạn đang downcasting nó kinda làm cho tinh thần ..? – flq

+2

Chỉ để làm cho nó rõ ràng - nó là viên đạn cuối cùng của bạn "một chuyển đổi tham chiếu ngầm định tồn tại từ TBase sang IBase" là không đúng sự thật (trừ khi bạn thêm ': class'). Nó có thể được gán, nhưng nó là * không * nhất thiết là một chuyển đổi tham chiếu. Không có ': class', nó là một chuyển đổi" hạn chế ", là một phép thuật cho phép các phương thức gọi IL giống nhau (bao gồm cả các trình truy cập thuộc tính) trên các kiểu tham chiếu và kiểu giá trị theo cùng một cách: http://msdn.microsoft .com/vi-us/library/system.reflection.emit.opcodes.constrained.aspx –

+0

Charles: bạn sai - công việc chuyển nhượng đầu tiên (Hoạt động trên Máy của tôi (TM)). –

Trả lời

49

Phương sai chỉ hoạt động đối với các loại tham chiếu (hoặc có một chuyển đổi danh tính). Người ta không biết rằng TBase là kiểu tham chiếu, trừ khi bạn thêm : class:

public void Foo<TBase>() where TBase : class, IBase 

kể từ khi tôi có thể viết một:

public struct Evil : IBase {} 
+0

Câu trả lời hay - thêm công việc ràng buộc lớp. Tuy nhiên - điều này đặt ra một câu hỏi khác: tại sao nhiệm vụ đầu tiên lại hoạt động? –

+3

@Omer - vì 'IBase' và' IDerived' * được * coi là tham chiếu; nó chỉ là 'TBase' chưa được quyết định. –

+0

@MarcGravell: Để chính xác hơn, trong khi các giao diện có thể được thực hiện bởi các loại không phải giá trị (heap), một vị trí lưu trữ của một kiểu giao diện sẽ luôn * giữ một tham chiếu đối tượng đống. Ví dụ, một 'List >' giữ tham chiếu đến các đối tượng heap thực hiện 'IEnumerator ', trong khi 'List trong đó T: IEnumerator ' sẽ, nếu 'T' là' List .Enumerator', giữ các thể hiện của loại cấu trúc đó thay thế. Lưu ý rằng nếu một cố gắng lưu trữ một 'List .Enumerator' trong một' List > ', hệ thống sẽ sao chép các trường của nó sang một đối tượng heap mới và lưu trữ một tham chiếu đến điều đó. – supercat

13

Marc là đúng - Tôi đã chỉ là về để dán phản ứng tương tự.

Xem các Hiệp phương sai & Contravariance FAQ:

http://blogs.msdn.com/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

Từ FAQ:

"Chênh lệch chỉ được hỗ trợ nếu một tham số type là một kiểu tham khảo."

Phương sai không được hỗ trợ cho các loại giá trị

Sau đây không biên dịch hoặc là:

// int is a value type, so the code doesn't compile. 
IEnumerable<Object> objects = new List<int>(); // Compiler error here. 
+0

Liên kết hỗ trợ tốt - cảm ơn –

+0

Và nhiều hơn nữa phù hợp với câu hỏi, những điều sau đây sẽ không hoạt động: 'IEnumerable comparables = new List ();' –