2013-07-30 220 views
8

上下文:.NET 4.0,C#C#使用泛型和接口實現

我創建了一組接口和一組實現它們來提供某些服務的接口。客戶端使用具體類,但調用使用接口聲明的方法作爲參數類型。

一個簡單的例子是這樣的一個:

namespace TestGenerics 
{ 
    // Interface, of fields 
    interface IField 
    { 
    } 

    // Interface: Forms (contains fields) 
    interface IForm<T> where T : IField 
    { 

    } 

    // CONCRETE CLASES 
    class Field : IField 
    { 
    } 

    class Form <T> : IForm<T> where T : IField 
    { 
    } 

    // TEST PROGRAM 
    class Program 
    { 
     // THIS IS THE SIGNATURE OF THE METHOD I WANT TO CALL 
     // parameters are causing the error. 
     public static void TestMethod(IForm<IField> form) 
     { 
      int i = 1; 
      i = i * 5; 
     } 

     static void Main(string[] args) 
     { 
      Form<Field> b = new Form<Field>(); 
      Program.TestMethod(b); 
     } 
    } 
} 

的代碼對我來說很有意義,但我得到的編譯器錯誤:

Argument 1: cannot convert from ' TestGenerics.Form<TestGenerics.Field> ' to ' TestGenerics.IForm<TestGenerics.IField> ' TestGenerics

我不知道我做錯了什麼,我在網上看了很多頁面,但沒有解決我的問題。

是否有不會改變什麼,我試圖建立,大部分的架構的解決方案:

編輯:我設計的接口的方式,使得他們應該獨立於實現它們的具體clases的。具體的clases可以從DLL加載,但大多數應用程序與接口一起工作。在某些情況下,我需要使用具體的clases,特別是在使用需要序列化的clases時。

在此先感謝。

亞歷

+5

這會在協方差的境界之下。具體來說,如果將['out' generic modifier](http://msdn.microsoft.com/zh-cn/library/dd469487.aspx)添加到您的簽名'interface IForm where T:IField',那麼它將工作。但是這增加了其他限制/考慮因素,所以我不能評論它是否適用於您當前的設計/使用。 –

+2

我很好奇你在用這個設計來完成什麼。 – sidesinger

+0

非常感謝這兩個肛門,他們真的很好。我添加了一個編輯,解釋爲什麼我以這種方式設計瞭解決方案,但是這兩種方法都可以解決我的問題。問候。 – Sugar

回答

13

的問題是,Form<Field>實現IForm<Field>但不IForm<IField>。除非它被標記爲與out標識符協變,否則不能使用繼承類(或接口)作爲通用參數。但是,將界面標記爲協變會嚴重限制使用(主要是在「僅輸出」界面中製作,例如IEnumerable),因此它可能不適用於您。

一種方式來得到它的工作就是讓TestMethod通用以及:

public static void TestMethod<T>(IForm<T> form) where T:IField 
{ 
    int i = 1; 
    i = i * 5; 
} 
10

您可以使用協方差,像這樣:

interface IForm<out T> where T : IField 
{ 

} 

更多關於協變和逆變here

+1

協方差,當您允許派生類用作使用「out」關鍵字的泛型類型參數。在我發佈的鏈接中,您可以看到以下語句: 「因爲IEnumerable接口的類型參數是協變的,所以可以將IEnumerable (IEnumerable(Of Derived)在Visual Basic中)的實例分配給IEnumerable類型的變量 「 –

7

其他人指出了錯誤消息背後的原因,但讓我們暫時檢查示例代碼的設計。也許你在沒有需要的情況下使用泛型。

你已經說過你正在使用在IField接口中聲明的方法,所以可能不需要使你的IForm類是通用的 - 只需要存儲對IField的引用,而不是通用參數'T'(無論如何它已經保證是一個IField)。

例如,使用方法:

public interface IForm 
{ 
    IEnumerable<IField> Fields { get; set; } 
} 

,而不是

public interface IForm<T> where T : IField 
{ 
    IEnumerable<T> Fields { get; set; } 
} 
+0

同意。另一個跡象是消費者代碼將這個實例化爲IForm 和表格的形式,表明IField是足夠的。真正的測試無論是否具體類型T都需要成爲面向外部API的一部分。 – Tormod