【问题标题】:Implementing generic methods from an interface using another interface使用另一个接口从一个接口实现泛型方法
【发布时间】:2016-08-01 15:48:50
【问题描述】:

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

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

数据库接口,其中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&lt;T&gt;()' 的类型参数无法从用法中推断出来。尝试明确指定类型参数。

2)

无法将类型 'System.Collections.Generic.List&lt;TaskElement&gt;' 隐式转换为 'System.Collections.Generic.List&lt;T&gt;'

我尝试将方法更改为使用显式类型,但在那里遇到了问题。如果我使用IElement(我的所有元素的通用接口)我不能返回一个TaskElement对象列表(我的IElement实现),因为它与返回类型(List&lt;IElement&gt;)不匹配,如果我改变List&lt;TaskElement&gt; 的返回类型我不再实现接口。

值得注意的是,如果我停止使用接口和泛型,我可以很容易地让它工作,但这似乎是使用接口的理想情况。当另一个应用程序(如直接继承)可能更好时,也许我正在努力将很多东西塞进一个接口?

问题

如何实现一个具有通用返回值的接口,同时限制可以返回的类型仅用于另一个接口的实现。

【问题讨论】:

  • 你试过var returnList = new List&lt;T&gt;();
  • 请注意,在实现接口时查看您的 TaskElement,您必须将 Id 设为公开(默认情况下 c# 将其设置为私有)
  • 与您的问题无关,但如果您刚刚开始 c#,您应该阅读coding conventions 其他开发人员会感谢您!
  • 你的方法签名是List&lt;T&gt; listTasks&lt;T&gt;() where T : IElement。这意味着接口的任何实现都应该能够与实现IElement 的任何类型一起使用。这是故意的吗?
  • 也许你需要让接口本身像public interface IDatabase&lt;T&gt; where T:IElement一样通用?

标签: c# generics interface


【解决方案1】:

让我们仔细看看你对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&lt;TaskElement&gt;()。那只能给你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&lt;T&gt; 有一个new 约束,这意味着它只能被赋予具有零参数构造函数的类型(因为该方法将创建给定类的实例)。

【讨论】:

  • 哦,太好了,这很奏效。我所有的挖掘,我从来没有看到过新的参考。顺便说一句,它需要是public List&lt;T&gt; listItems&lt;T&gt;() where T : IElement, new(),并且还要添加到接口中,以便维护接口和实现之间的契约。这也使该方法完全通用,这是我第二步要做的事情!非常感谢@Kyle!
【解决方案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;
    }

【讨论】:

  • 我试了一下,但不幸的是 SQLiteConnection Table 需要非抽象类型。这就是为什么我想使用显式类型 TaskElement(从 IElement 实现),因为它定义了 SQL 表并且是 Table 方法所必需的。
  • @NigelDH - 我很困惑你将如何实际使用它 - 假设你的类将返回不仅仅是 TaskElement 你将如何将该方法编码为足够通用以返回 OtherElementThirdElement 如果Table 调用需要具体类型?
  • 您实际上是完全正确的。我正在做一个像这样的更静态的实现,只是为了让我了解泛型,但理想情况下,我想将一个参数传递给这样的方法:listItems(IElement type),然后使用它来定义要搜索的表。我还将扩展它以包括用于搜索表等的选项。现在尽量保持简单。
【解决方案3】:

我认为你是在正确的轨道上明确定义你的列表是这样的:

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

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

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

【讨论】:

    【解决方案4】:

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

    代替

    var returnList = new List<IElement>();
    

    这样做

    var returnList = new List<T>();
    

    【讨论】:

    • 这还不够,因为您无法将TaskElement 的实例添加到returnList
    猜你喜欢
    • 2021-10-09
    • 2021-11-01
    • 2019-04-06
    • 2018-07-23
    • 1970-01-01
    • 2022-11-28
    • 2020-06-21
    • 1970-01-01
    • 2013-10-15
    相关资源
    最近更新 更多