我试图创建一个通用接口,它允许我使用与数据库交互的方法。我希望我的业务应用程序能够实例化任何连接方法,并确保接口是相同的。
这是我现在尝试的简化版本。
数据库接口,其中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>
,我将不再实施该界面。
值得注意的是,如果我停止使用界面和泛型,我可以轻松地将其工作,但这似乎是使用界面的理想情况。也许当其他应用程序(如直接继承)可能更好时,我会努力将很多东西塞进接口?
问题
如何实现具有泛型返回值的接口,同时限制只能返回到另一个接口的实现的类型。
答案 0 :(得分: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
约束,这意味着它只能被赋予具有零参数构造函数的类型(因为该方法将创建给定类的实例)
答案 1 :(得分: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;
}
答案 2 :(得分: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()
答案 3 :(得分:0)
创建对象实例时需要使用Generic类型:
而不是
var returnList = new List<IElement>();
这样做
var returnList = new List<T>();