2012-02-20 32 views
13

UPDATE: Các mã sau đây chỉ có ý nghĩa trong C# 4.0 (Visual Studio 2010)Hiệp phương sai/contravariance: làm thế nào để làm cho đoạn mã sau biên dịch

Nó có vẻ như tôi đang gặp một số hiểu lầm của hiệp phương sai/điều contravariance . Ai có thể cho tôi biết tại sao mã sau đây không biên dịch?

public class TestOne<TBase> 
{ 
    public IEnumerable<TBase> Method<TDerived>(IEnumerable<TDerived> values) 
     where TDerived: TBase 
    { 
     return values; 
    } 
} 

trong khi một biên dịch này: (!!!)

public interface IBase 
{ 
} 
public interface IDerived: IBase 
{ 
} 
public class TestTwo 
{ 
    public IEnumerable<IBase> Method(IEnumerable<IDerived> values) 
    { 
     return values; 
    } 
} 

Trả lời

13

Hiệp phương sai chỉ áp dụng để tham khảo các loại (cho các đối số loại), vì vậy bạn cần phải thêm một hạn chế lớp:

public IEnumerable<TBase> Method<TDerived>(IEnumerable<TDerived> values) 
    where TDerived : class, TBase 
{ 
    return values; 
} 

Điều này sẽ ngăn bạn cố gắng chuyển đổi, ví dụ: IEnumerable<int> thành số IEnumerable<object>, không hợp lệ.

+0

@ Adam: Tôi tin rằng bạn là sai, 'IEnumerable ' là không 'IEnumerable ' theo mặc định vì vậy nó sẽ không biên dịch thậm chí trong '3.5' – sll

+1

@AdamMihalcin: Không, đó là mã * sẽ không * đã biên dịch trước .NET 4. Tôi đã thử bản thân mình để xác minh điều đó. Nếu không có sự bất biến chung chung, việc chuyển đổi từ 'IEnumerable 'thành' IEnumerable 'chỉ đơn giản là không hợp lệ. –

+0

cuộc gọi tốt trên TSuper, chỉ cần cố định rằng –

1

Tôi không thể nghĩ ra bất kỳ tình huống nào bạn thực sự cần TDerived. Sử dụng TBase là đủ:

public class TestOne<TBase> 
{ 
    public IEnumerable<TBase> Method(IEnumerable<TBase> values) 
    { 
     return values; 
    } 
} 

Sau khi tất cả, bạn không có thông tin về TDerived ngoài thực tế là nó là một TBase ...

+2

thật không may là nó không phải là thứ tôi tạo ra vì sự phức tạp, đó là một tình huống thực sự mà tôi đã có trong mã của tôi –

+1

@bonomo: Thật thú vị, tôi rất thích xem một ví dụ chi tiết hơn để tham khảo trong tương lai. :-) – linepogl

0

Không biên soạn cho tôi ban đầu. Cả hai đều thất bại trên dàn diễn viên ngầm từ Super (T/I) đến Base (T/I). Tuy nhiên, khi tôi thêm một trường hợp rõ ràng, cả hai được biên dịch.

public IEnumerable<TBase> Method<TSuper>(IEnumerable<TSuper> values) 
    where TSuper: TBase 
    { 
     return (IEnumerable<TBase>) values; 
    } 
Các vấn đề liên quan