2016-08-01 150 views
5

我正在嘗試創建一個通用接口,這將允許我與數據庫進行交互的方法。我希望我的業務應用程序能夠實例化任何連接方法,並確保界面完全相同。使用另一個接口從接口實現通用方法

下面是我現在嘗試的簡化版本。

數據庫接口其中IElement是定義表的另一個接口。

public interface IDatabase 
{ 
    void setItem(IElement task); //this works fine 
    List<T> listTasks<T>() where T : IElement; // this doesn't 
} 

IElement接口:

public interface IElement 
{ 
    int id { get; set; } 
} 

IElement的執行情況:

public class TaskElement: IElement 
{ 
    public int id { get; set; } 
    public string name {get; set; } 
} 

的了IDatabase實現:

public class SQLiteDb: IDatabase 
{ 
    public SqLiteDb(SQLiteConnection conn) 
    { 
     database = conn; 
    } 

    public void setItem(IElement task) 
    { 
     // works fine when passed a new TaskElement() which is an implementation of IElement. 
     database.Insert(task); 
    } 

    //it all goes off the rails here 
    public List<T> listItems<T>() where T : IElement 
    { 
     var returnList = new List<IElement> 

     foreach (var s in database.Table<TaskElement>()) 
     { returnList.Add(s); } 

     return returnList; 
    } 

我已經嘗試了很多的變化上這一點,但每一個給我一個新的是起訴。例如,在這裏有兩個錯誤。

1)

的類型參數方法'SQLiteDb.listTasks<T>()'不能從使用推斷。嘗試明確指定類型參數。

2)

無法隱式轉換類型'System.Collections.Generic.List<TaskElement>''System.Collections.Generic.List<T>'

我試着改變使用顯式類型的方法,但也有過問題。如果我使用IElement(我所有元素的通用接口),我無法返回TaskElement對象的列表(我的實現爲IElement),因爲它與返回類型(List<IElement>)不匹配,並且如果我將返回類型更改爲List<TaskElement> I不再實現接口。

值得注意的是,如果我停止使用接口和泛型,我可以很容易地實現這一點,但這似乎是使用接口的理想情況。當另一個應用程序(如直接繼承)可能更好時,也許我試圖將很多東西塞進界面中?

問題

我怎麼能同時限制它可以返回到另一個接口的唯一實現的類型實現一個通用的返回值的接口。

+0

您是否嘗試過'var returnList = new List ();' –

+0

只是爲了注意,看看您的TaskElement,因爲您正在實現一個接口,您必須使Id公開(因爲默認情況下,c#將其設置爲私有) – ams4fy

+3

與你的問題沒有任何關係,但如果你剛剛啓動c#,你應該閱讀[編碼約定](https://msdn.microsoft.com/en-gb/library/ff926074.aspx)其他開發人員會感謝您! – Jamiec

回答

3

讓我們爲您實現listItems仔細一看:

public List<T> listItems<T>() where T : IElement 
{ 
    var returnList = new List<IElement> 

    foreach (var s in database.Table<TaskElement>()) 
    { returnList.Add(s); } 

    return returnList; 
} 

你在這裏做什麼是寫了一個方法,其中只要該類型實現IElement,則允許呼叫者詢問他們想要的任何類型。但是你的方法中的代碼並沒有給他們一個他們想要的類型列表,它給了他們一個列表IElement。所以這違反了合同。

但問題的真正根源是database.Table<TaskElement>()。那隻能給你TaskElement的實例。你需要做的是T,但要做到這一點,你需要一個額外的通用的限制:

public List<T> listItems<T>() where T : IElement, new 
{ 
    var returnList = new List<T> 

    foreach (var s in database.Table<T>()) 
    { 
     returnList.Add(s); 
    } 

    return returnList; 
} 

這是因爲database.Table<T>new約束,這意味着它只能被給予具有零參數的構造函數類型(因爲該方法將創建給定類的實例)。

+0

哦,太棒了,那工作的一種享受。我所有的挖掘,我從來沒有看到一個新的參考。順便說一句,它需要的是 '公開名單 listItems中()其中T:IElement,新的()' 也加入使維持接口和實現之間的合同的接口。這也使得該方法完全通用,這是我第二步要做的事情!非常感謝@凱爾! –

0

您需要使用泛型類型創建對象實例時:

而不是

var returnList = new List<IElement>(); 

做這個

var returnList = new List<T>(); 
+0

這是不夠的,因爲您無法將'TaskElement'的實例添加到'returnList'。 – Lee

2

我相信它應該是這樣的

public List<T> listItems<T>() where T : IElement 
    { 
     var returnList = new List<T> 

     foreach (var s in database.Table<T>()) 
     { returnList.Add(s); } 

     return returnList; 
    } 
+0

我給了一個鏡頭,但不幸的是SQLiteConnection表需要一個非抽象類型。這就是爲什麼我想使用顯式類型TaskElement(它是從IElement實現的),因爲它定義了SQL表並且是Table方法所要求的。 –

+0

@NigelDH - 我很困惑你會如何實際使它可用 - 假設你的類將返回的不僅僅是'TaskElement'你將如何編寫該方法足夠通用以返回'OtherElement'或'ThirdElement'如果'表格調用需要一個具體的類型? – Jamiec

+0

你其實是完全正確的。我正在做一個像這樣的更靜態的實現,只是爲了讓我的頭在泛型上,但理想情況下,我想將一個參數傳遞給像這樣的方法:listItems(IElement類型),然後用它來定義搜索哪個表。我也將這個擴展爲包括搜索表格的選項等。試圖保持現在簡單。 –

1

,我認爲你是在正確的軌道上有明確定義你這樣的名單:

public interface IDatabase 
{ 
    void setItem(IElement task); //this works fine 
    List<IElement> listTasks<IElement>(); 
} 

既然你不能直接演員名單<TaskElement>列出<IElement>你必須做一個轉換在listTasks方法。這裏推薦了幾種方法:Shorter syntax for casting from a List<X> to a List<Y>?。我認爲LINQ的方法是最簡單的,如果你確定與使用LINQ:

List<IElement> listOfIElement = listOfTaskElement.Cast<IElement>().ToList()