【问题标题】:How to return interface using generic in C#如何在 C# 中使用泛型返回接口
【发布时间】:2020-01-23 18:40:41
【问题描述】:

我是 C# 泛型概念的新手,我想使用泛型概念返回接口实现类。下面是我目前没有泛型实现的示例:

1)返回接口的工厂类,该类有两个接受不同数据模型的重载方法:

public class Factory
{
    public ICommon Init(DBInfoData dbInfoData)
    {
        return new ClassA(dbInfoData);
    }

    public ICommon Init(WebInfoData webInfoData)
    {
        return new ClassB(webInfoData);
    }
}

2) 接口和接口实现两个类如下:

//=== Common Interface
public interface ICommon
{
    void MethodA();
    void MethodB();
}

//=== Internal access only ClassA
internal class ClassA : ICommon
{
    private DBInfoData _DBInfoData = null;
    public ClassA(DBInfoData dbInfoData)
    {
        _DBInfoData = dbInfoData;
    }

    public void MethodA()
    {
        throw new NotImplementedException();
    }

    public void MethodB()
    {
        throw new NotImplementedException();
    }
}

//=== Internal access only ClassB
internal class ClassB : ICommon
{
    private WebInfoData _WebInfoData = null;

    public ClassB(WebInfoData webInfoData)
    {
        _WebInfoData = webInfoData;
    }

    public void MethodA()
    {
        throw new NotImplementedException();
    }

    public void MethodB()
    {
        throw new NotImplementedException();
    }
}

3) 数据模型类如下:

//=== Database Information
public class DBInfoData
{
    public string Server { get; set; }
    public string Database { get; set; }
}

//=== Web Server Information
public class WebInfoData
{
    public string URL { get; set; }
    public int Port { get; set; }
}

现在我想实现 C# 的通用功能,在工厂类中我不想声明两个重载方法。使用单一方法,我可以根据数据模型传递返回 ClassA 或 ClassB。

【问题讨论】:

  • 您现在是如何调用Factory 方法的?您只有两个数据模型,还是更多?
  • “我不想声明两个重载方法”。这是为什么?您向我们展示的代码干净且易于理解。在这种情况下,我看不出使用泛型有什么好处。您是否在 Factory 中遗漏了您想要删除的重复代码的某些部分?
  • 第二个@GeorgPatscheider 的评论。除非有你没有分享的东西,否则泛型在这里没有任何收获。坚持重载。
  • @GeorgPatscheider:据说想要实现通用概念,这将减少重载方法,并且通过单一方法可以绑定适当的类。我在工厂代码中没有遗漏任何部分。我喜欢基于数据模型绑定类,所以我正在研究如何在其中实现通用概念。

标签: c# generic-programming


【解决方案1】:

您可以编辑Init 方法,而无需编辑任何其他内容。此方法将采用泛型类型参数T,它可以是任何类型。然后你可以使用is 操作符,它根据docs 用于类型测试。但是,您需要检查 T 的任何不受支持的类型,因为您没有向传递的泛型类型添加任何约束。一个原始的实现是:

public class Factory
{
    public ICommon Init<T>(T infoData)
    {
        if (infoData is DBInfoData dbInfoData) {
            return new ClassA(dbInfoData);
        }
        if (infoData is WebInfoData webInfoData) {
            return new ClassB(webInfoData);
        }

        throw new Exception($"Cannot create instance for info data of type {infoData.GetType().Name}");
    }
}

并对其进行测试:

    var factory = new Factory();

    var t1 = factory.Init(new DBInfoData()); // will be ClassA
    var t2 = factory.Init(new WebInfoData()); // ClassB

为了使其更加复杂,您可以在泛型 T 类中引入 type constraint,以确保您只能传递适当的类型。对于当前情况,您可以通过引入一个空接口例如IInfoData 为您的类DBInfoDataWebInfoData 创建一个marker interface。然后你必须像这样继承你的类:

public interface IInfoData {}

public class DBInfoData : IInfoData
{
    public string Server { get; set; }
    public string Database { get; set; }
}

public class WebInfoData : IInfoData
{
    public string URL { get; set; }
    public int Port { get; set; }
}

现在两者都继承自(实际上是“标记为”)您的基本接口。通过添加我在上面链接的文档中显示的约束,向您的工厂引入一个约束,以仅允许 IInfoData 的后代作为参数传递(因此 DBInfoDataWebInfoData):

public class Factory
{
    public ICommon Init<T>(T infoData) where T: IInfoData
    {
        if (infoData is DBInfoData dbInfoData) {
            return new ClassA(dbInfoData);
        }
        if (infoData is WebInfoData webInfoData) {
            return new ClassB(webInfoData);
        }

        throw new Exception($"Cannot create instance for info data of type {infoData.GetType().Name}");
    }
}

IInfoData 的后代以外的任何类型都会导致编译错误,您就完成了。像我之前的例子一样使用它:

    var factory = new Factory();

    var t1 = factory.Init(new DBInfoData()); // will be ClassA
    var t2 = factory.Init(new WebInfoData()); // ClassB

【讨论】:

  • 虽然这行得通,但它违反了 SOLID 原则,就像您引入了 IInfoData 的新实现一样,您需要更新您的 Init 方法。话虽如此,您的代码根本不是通用的。它是一些特定的实现,隐藏在通用接口后面。
  • 是的,就像工厂模式本身在某种程度上违反了 SOLID 原则一样,如果按照它的意图来实现。见softwareengineering.stackexchange.com/questions/302780/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-13
相关资源
最近更新 更多