在c#中隐式和显式实现接口有什么不同?

什么时候用隐式,什么时候用显式?

这两者之间有什么利弊吗?


微软的官方指南(来自第一版框架设计指南)指出,不建议使用显式实现,因为它会给代码带来意想不到的行为。

我认为这个准则在前ioc时代是非常有效的,当你不把东西作为接口传递的时候。

有人能谈谈这方面的问题吗?


当前回答

我发现自己最近更经常地使用显式实现,原因如下:

Always using explicit from the starts prevents having any naming collisions, in which explicit implementation would be required anyways Consumers are "forced" to use the interface instead of the implementation (aka not "programming to an implementation") which they should / must do anyways when you're using DI No "zombie" members in the implementations - removing any member from the interface declaration will result in compiler errors if not removed from the implementation too Default values for optional parameters, as well constraints on generic arguments are automatically adopted - no need to write them twice and keep them in sync

其他回答

显式接口实现的一个重要用途是在需要实现具有混合可见性的接口时。

这个问题和解决方案在c#内部接口这篇文章中有很好的解释。

例如,如果您希望保护应用程序层之间对象的泄漏,该技术允许您指定可能导致泄漏的成员的不同可见性。

我发现自己最近更经常地使用显式实现,原因如下:

Always using explicit from the starts prevents having any naming collisions, in which explicit implementation would be required anyways Consumers are "forced" to use the interface instead of the implementation (aka not "programming to an implementation") which they should / must do anyways when you're using DI No "zombie" members in the implementations - removing any member from the interface declaration will result in compiler errors if not removed from the implementation too Default values for optional parameters, as well constraints on generic arguments are automatically adopted - no need to write them twice and keep them in sync

除了已经提供的优秀答案之外,还有一些情况需要显式实现,以便编译器能够找出需要什么。以IEnumerable<T>为例,它可能会经常出现。

这里有一个例子:

public abstract class StringList : IEnumerable<string>
{
    private string[] _list = new string[] {"foo", "bar", "baz"};

    // ...

    #region IEnumerable<string> Members
    public IEnumerator<string> GetEnumerator()
    {
        foreach (string s in _list)
        { yield return s; }
    }
    #endregion

    #region IEnumerable Members
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
    #endregion
}

这里,IEnumerable<string>实现了IEnumerable,因此我们也需要。但是等等,泛型和普通版本都实现了具有相同方法签名的函数(c#对此忽略了返回类型)。这是完全合法的。编译器如何解决使用哪个?它迫使您最多只有一个隐式定义,然后它就可以解决它需要解决的任何问题。

ie.

StringList sl = new StringList();

// uses the implicit definition.
IEnumerator<string> enumerableString = sl.GetEnumerator();
// same as above, only a little more explicit.
IEnumerator<string> enumerableString2 = ((IEnumerable<string>)sl).GetEnumerator();
// returns the same as above, but via the explicit definition
IEnumerator enumerableStuff = ((IEnumerable)sl).GetEnumerator();

PS: IEnumerable的显式定义中有一点间接的作用,因为在函数内部,编译器知道变量的实际类型是StringList,这就是它解析函数调用的方式。实现一些抽象层,一些。net核心接口似乎已经积累起来了。

如果显式实现,则只能通过与接口类型相同的引用引用接口成员。实现类类型的引用不会公开这些接口成员。

如果实现的类不是公共的,除了用于创建类的方法(可以是工厂或IoC容器),并且除了接口方法(当然),那么我认为显式实现接口没有任何好处。

否则,显式实现接口将确保不使用对具体实现类的引用,从而允许您在以后更改该实现。“确保”,我想,是“优势”。一个分解良好的实现可以在没有显式实现的情况下完成这一点。

在我看来,缺点是您会发现自己在实现代码中对接口进行类型转换,而接口可以访问非公共成员。

就像许多事情一样,优势就是劣势(反之亦然)。显式实现接口将确保不会公开具体的类实现代码。

隐式定义是将接口需要的方法/属性等直接作为公共方法添加到类中。

显式定义强制只在直接使用接口而不是底层实现时才公开成员。在大多数情况下,这是首选。

By working directly with the interface, you are not acknowledging, and coupling your code to the underlying implementation. In the event that you already have, say, a public property Name in your code and you want to implement an interface that also has a Name property, doing it explicitly will keep the two separate. Even if they were doing the same thing I'd still delegate the explicit call to the Name property. You never know, you may want to change how Name works for the normal class and how Name, the interface property works later on. If you implement an interface implicitly then your class now exposes new behaviours that might only be relevant to a client of the interface and it means you aren't keeping your classes succinct enough (my opinion).