使用另一个接口从接口实现泛型方法

问题描述 投票:5回答:4

我正在尝试创建一个通用接口,它允许我使用与数据库交互的方法。我希望我的业务应用程序能够实例化任何连接方法,并确保接口是相同的。

这是我现在正在尝试的简化版本。

数据库接口,其中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>我我不再实施界面了。

值得注意的是,如果我停止使用界面和泛型,我可以轻松地将其工作,但这似乎是使用界面的理想情况。也许当其他应用程序(如直接继承)可能更好时,我正努力将很多东西塞进接口中?

如何实现具有泛型返回值的接口,同时限制只能返回到另一个接口的实现的类型。

c# generics interface
4个回答
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约束,这意味着它只能被赋予具有零参数构造函数的类型(因为该方法将创建给定类的实例)。


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;
    }

1
投票

我认为你是在正确的轨道上明确定义你的列表,如下所示:

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

由于您无法直接将List <TaskElement>强制转换为List <IElement>,因此您必须在listTasks方法中进行转换。这里推荐几种方法:Shorter syntax for casting from a List<X> to a List<Y>?。我认为Linq方法是最简单的,如果你可以使用Linq:

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

0
投票

创建对象实例时需要使用Generic类型:

代替

var returnList = new List<IElement>();

做这个

var returnList = new List<T>();
© www.soinside.com 2019 - 2024. All rights reserved.