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

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

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


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

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

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


当前回答

实现接口的每个类成员都导出一个语义上类似于VB的声明。NET接口声明被编写,例如:

Public Overridable Function Foo() As Integer Implements IFoo.Foo

尽管类成员的名称经常与接口成员的名称相匹配,而且类成员通常是public的,但这些都不是必需的。你也可以声明:

Protected Overridable Function IFoo_Foo() As Integer Implements IFoo.Foo

In which case the class and its derivatives would be allowed to access a class member using the name IFoo_Foo, but the outside world would only be able to access that particular member by casting to IFoo. Such an approach is often good in cases where an interface method will have specified behavior on all implementations, but useful behavior on only some [e.g. the specified behavior for a read-only collection's IList<T>.Add method is to throw NotSupportedException]. Unfortunately, the only proper way to implement the interface in C# is:

int IFoo.Foo() { return IFoo_Foo(); }
protected virtual int IFoo_Foo() { ... real code goes here ... }

没那么好。

其他回答

实现接口的每个类成员都导出一个语义上类似于VB的声明。NET接口声明被编写,例如:

Public Overridable Function Foo() As Integer Implements IFoo.Foo

尽管类成员的名称经常与接口成员的名称相匹配,而且类成员通常是public的,但这些都不是必需的。你也可以声明:

Protected Overridable Function IFoo_Foo() As Integer Implements IFoo.Foo

In which case the class and its derivatives would be allowed to access a class member using the name IFoo_Foo, but the outside world would only be able to access that particular member by casting to IFoo. Such an approach is often good in cases where an interface method will have specified behavior on all implementations, but useful behavior on only some [e.g. the specified behavior for a read-only collection's IList<T>.Add method is to throw NotSupportedException]. Unfortunately, the only proper way to implement the interface in C# is:

int IFoo.Foo() { return IFoo_Foo(); }
protected virtual int IFoo_Foo() { ... real code goes here ... }

没那么好。

引用Jeffrey Richter从CLR通过c#编写的 (EIMI的意思是显式接口方法实现)

It is critically important for you to understand some ramifications that exist when using EIMIs. And because of these ramifications, you should try to avoid EIMIs as much as possible. Fortunately, generic interfaces help you avoid EIMIs quite a bit. But there may still be times when you will need to use them (such as implementing two interface methods with the same name and signature). Here are the big problems with EIMIs: There is no documentation explaining how a type specifically implements an EIMI method, and there is no Microsoft Visual Studio IntelliSense support. Value type instances are boxed when cast to an interface. An EIMI cannot be called by a derived type.

如果您使用接口引用,ANY虚链可以在任何派生类上显式地替换为EIMI,并且当这种类型的对象强制转换到接口时,您的虚链将被忽略,并调用显式实现。这根本不是多态性。

EIMIs还可以用于从基本框架接口的实现(如IEnumerable<T>)中隐藏非强类型接口成员,这样你的类就不会直接公开非强类型方法,但在语法上是正确的。

除了已经提供的优秀答案之外,还有一些情况需要显式实现,以便编译器能够找出需要什么。以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核心接口似乎已经积累起来了。

隐式是指通过类上的成员定义接口。显式是指在接口上的类中定义方法。我知道这听起来令人困惑,但我的意思是:IList。CopyTo将隐式实现为:

public void CopyTo(Array array, int index)
{
    throw new NotImplementedException();
}

并明确为:

void ICollection.CopyTo(Array array, int index)
{
    throw new NotImplementedException();
}

不同之处在于,隐式实现允许您通过创建的类访问接口,方法是将接口转换为该类和接口本身。显式实现允许您仅通过将接口转换为接口本身来访问接口。

MyClass myClass = new MyClass(); // Declared as concrete class
myclass.CopyTo //invalid with explicit
((IList)myClass).CopyTo //valid with explicit.

我使用显式主要是为了保持实现的简洁,或者当我需要两个实现时。不管怎样,我很少使用它。

我相信有更多的理由使用/不使用explicit,其他人会发布。

请参阅本帖的下一篇文章,了解每一篇文章背后的优秀推理。

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

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