2013-03-04 45 views
4

我有一個通用的接口彼此相連。接口通用層次

public interface IA 
{ 
    int val { get; set; } 
} 
public interface IB<T> where T:IA 
{ 
    T a_val { get; set; } 
} 
public interface IC<T> where T : IB<IA> 
{ 
    T b_val { get; set; } 
} 

public class a:IA 
{ 
    public int val { get; set; } 
} 
public class b:IB<a> 
{ 
    public a a_val { get; set; } 
} 
public class c:IC<b> 
{ 
    public b b_val { get; set; } 
} 

在過去的C類,我有一個錯誤:

The type 'b' cannot be used as type parameter 'T' in the generic type or method 'IC'. There is no implicit reference conversion from 'b' to 'IB'.

我如何正確使用通用的接口,在這種情況下?

回答

3

我不知道這是可以做到容易(多一點乾淨),但是這個代碼編譯:

public interface IA 
{ 
    int val { get; set; } 
} 
public interface IB<T> where T : IA 
{ 
    T a_val { get; set; } 
} 
public interface IC<T, U> where T : IB<U> where U : IA 
{ 
    T b_val { get; set; } 
} 

public class a : IA 
{ 
    public int val { get; set; } 
} 
public class b : IB<a> 
{ 
    public a a_val { get; set; } 
} 
public class c : IC<b, a> 
{ 
    public b b_val { get; set; } 
} 

什麼是更重要的,它不會讓你做什麼這樣的:

public class a1 : IA 
{ 
    public int val { get; set; } 
} 

public class c : IC<b, a1> 
{ 
    public b b_val { get; set; } 
} 

編譯器會引發以下錯誤:

The type 'ConsoleApplication2.b' cannot be used as type parameter 'T' in the generic type or method 'ConsoleApplication2.IC'. There is no implicit reference conversion from 'ConsoleApplication2.b' to 'ConsoleApplication2.IB'.

這真的很酷的功能,不是嗎?

+0

+1挖掘到那一個,你實際上必須鏈接的特異性的泛型。 – 2013-03-04 19:32:04

7

T in IC<T>必須是IB<IA>。你已經給它一個IB<A>。您不能保證IB<A>可以用作IB<IA>,因爲A實施了IA

想想這樣說:如果IB<T>的意思是「我可以吃T類型的東西」,並IA的意思是「我是果」,以及A意思是「蘋果」,然後IB<IA>的意思是「我可以吃任何水果」 ,而IB<A>的意思是「我可以吃任何蘋果」。如果某些代碼想要爲您提供香蕉和葡萄,那麼它需要採取IB<IA>而不是IB<A>

讓我們假設IB<A>可以轉換爲IB<IA>,看看有什麼不順心:

class AppleEater : IB<Apple> 
{ 
    public Apple a_val { get; set; } 
} 
class Apple : IA 
{ 
    public int val { get; set; } 
} 
class Orange : IA 
{ 
    public int val { get; set; } 
} 
... 
IB<Apple> iba = new AppleEater(); 
IB<IA> ibia = iba; // Suppose this were legal. 
ibia.a_val = new Orange(); // ibia.a_val is of type IA and Orange implements IA 

現在,我們剛剛成立iba.val,這是Apple類型的參考屬性Orange類型的對象。

這就是爲什麼轉換必須是非法的。

那麼你怎麼能使這個合法?

由於代碼的立場,你不能,因爲正如我剛纔所示,它不是類型安全的。

您可以通過將T標記爲out這樣使其合法:interface IB<out T>。然而,在任何「輸入上下文」中使用T是非法的。特別是,您不能擁有任何具有籌碼的T類型的任何財產。如果我們進行限制,則問題消失,因爲a_val不能設置爲Orange的實例,因爲它是隻讀的

這個問題在SO上非常頻繁地提出。在C#中查找關於「協方差和相反性」的問題,以獲得大量示例。

+0

引用Monty Python:「我的腦袋受傷了!」 – RBarryYoung 2013-03-04 19:45:37

+0

@RBarryYoung:你會想閱讀這個:http://blogs.msdn.com/b/ericlippert/archive/2007/10/24/5529183.aspx(請注意,這個系列是*協變前*在C#4.0中添加了反變換,所以我使用符號'<-T>'來表示''。當我編寫該系列時,我們還沒有確定語法。) – 2013-03-04 19:51:25

+0

ouch!我當然不希望成爲入門級程序員來維護這種事情。 :) – RBarryYoung 2013-03-04 19:55:57