依赖注入和显式接口实现

Posted

技术标签:

【中文标题】依赖注入和显式接口实现【英文标题】:Dependency Injection and Explicit Interface Implementation 【发布时间】:2015-10-18 21:59:06 【问题描述】:

在依赖注入方面显式实现接口是否有好处?

据我了解,接口可以显式或隐式实现:

interface IFoo

    void Bar();


//implicit implementation
class Foo1 : IFoo

    public void Bar()


//explicit implementation
class Foo2 : IFoo

    void IFoo.Bar()

现在显式实现只能通过调用接口方法来调用,而隐式实现可以直接在类的实例上调用:

class Baz

    void Ba()
    
        Foo1 foo1 = new Foo1();
        foo1.Bar();

        Foo2 foo2 = new Foo2();
        foo2.Bar();    //syntax error

        IFoo foo2_explicit = new Foo2();
        foo2_explicit.Bar();
    

因此,使用显式接口实现,不能意外调用具体类的方法,但必须调用接口方法。这是否会阻止紧密耦合的代码作为 DI 的一个目的,还是我在这里吠叫错误的树?毕竟,不能意外地编写一个构造函数或方法来注入一个具体的类而不是一个接口:

class Baz

    void Ba(Foo2 foo)
    
        foo.Bar(); //syntax error
    

    void Bb(IFoo foo)
    
        foo.Bar();
    

【问题讨论】:

您能否更新您的代码以使用 IFoo 而不是 Foo?在我看来这很令人困惑...... 完成 - 抱歉,我通常尊重代码约定... 【参考方案1】:

通常,依赖注入的目的是解耦,您可以通过将抽象注入其客户端来实现:

public class Baz

    private readonly IFoo foo;

    public Baz(IFoo foo)
    
        this.foo = foo;
    

    // Members using this.foo go here...

这确保Baz 依赖于IFoo,并且与任何具体实现解耦

具体类是否隐式或显式实现IFoo 没有区别。

偶尔,一个类可能有一个Concrete Dependency,但这不是特别正常;当它发生时,具体的依赖是concrete,所以通常根本不会实现接口。在这种情况下,显式与隐式接口实现无关。

【讨论】:

这就是我想要达到的目的。如果由于某种模糊的原因没有注意并编写了一个期望具体类的构造函数:public Baz(Foo1 foo),则使用显式接口实现将禁止对对象方法的任何调用。试图将一个具体的实现转换为它的接口会敲响一些警钟,不是吗? 如果你不小心添加了一个期望具体类的构造函数,那么你就失去了依赖注入的目的的解耦。这是服务的一个更大的问题,应该从问题的根源解决,而不是由某个任意客户端修补。【参考方案2】:

如果你的类在容器中,那么你使用接口。所以,没有任何好处。

但是,如果您直接使用您的类(例如在测试中),您必须强制转换才能访问该方法,这很不方便。

总计:在容器中使用类且不利于测试时有 0 个优势。

【讨论】:

【参考方案3】:

在我看来,一般来说,应该始终保留对类型“刚好”可以使用的对象的引用。考虑以下示例:

public interface IDo

    void Do();


public interface IWatch

    void Watch();


public class Foo : IDo, IWatch

    public void Dummy()  

    public void Watch()  

    public void Do()  

然后:

//I only want to use Do()
IDo aFoo = new Foo();

//I only want to use Watch()
IWatch bFoo = new Foo();

//I want to use stuff from Foo and optionally stuff from IDo or IWatch
Foo cFoo = new Foo();

在使用 MEF 或 Unity 等依赖注入容器时,您应该使用接口将对象导出到容器中,然后以相同的接口类型导入它。

按照这些模式,我并没有真正看到使用显式接口实现的好处。 (这也使得在文本编辑器上方的标准 Visual Studio 组合框中定位您的实现方法变得更加困难)

【讨论】:

以上是关于依赖注入和显式接口实现的主要内容,如果未能解决你的问题,请参考以下文章

C#接口的隐式和显式实现之间的区别[重复]

区别 b/w 在 C# 中隐式实现成员和显式实现成员 [重复]

基础知识系列?接口的显示实现和隐式实现

如何将依赖项注入实现接口的类中?

.NET依赖注入之一个接口多个实现

依赖注入的实现方式:设值注入和构造方法注入