2016-08-01 154 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()