C# 接口。隐式实现与显式实现

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/143405/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 15:26:31  来源:igfitidea点击:

C# Interfaces. Implicit implementation versus Explicit implementation

提问by Seb Nilsson

What are the differences in implementing interfaces implicitlyand explicitlyin C#?

在 C# 中隐式显式实现接口有什么区别?

When should you use implicit and when should you use explicit?

什么时候应该使用隐式,什么时候应该使用显式?

Are there any pros and/or cons to one or the other?

一个或另一个有什么优点和/或缺点吗?



Microsoft's official guidelines (from first edition Framework Design Guidelines) states that using explicit implementations are not recommended, since it gives the code unexpected behaviour.

Microsoft 的官方指南(来自第一版框架设计指南)指出不推荐使用显式实现,因为它会导致代码出现意外行为。

I think this guideline is very valid in a pre-IoC-time, when you don't pass things around as interfaces.

我认为这个指南在 IoC 之前的时代非常有效,当你不把东西作为接口传递时。

Could anyone touch on that aspect as well?

任何人都可以触及这一方面吗?

采纳答案by mattlant

Implicitis when you define your interface via a member on your class. Explicitis when you define methods within your class on the interface. I know that sounds confusing but here is what I mean: IList.CopyTowould be implicitly implemented as:

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

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

and explicitly as:

并明确为:

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

The difference is that implicit implementation allows you to access the interface through the class you created by casting the interface as that class and as the interface itself. Explicit implementation allows you to access the interface only by casting it as the interface itself.

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

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

I use explicit primarily to keep the implementation clean, or when I need two implementations. Regardless, I rarely use it.

我使用显式主要是为了保持实现干净,或者当我需要两个实现时。无论如何,我很少使用它。

I am sure there are more reasons to use/not use explicit that others will post.

我确信有更多理由使用/不使用其他人会发布的明确内容。

See the next postin this thread for excellent reasoning behind each.

请参阅此线程中的下一篇文章,了解每个文章背后的出色推理。

回答by Phil Bennett

Implicit definition would be to just add the methods / properties, etc. demanded by the interface directly to the class as public methods.

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

Explicit definition forces the members to be exposed only when you are working with the interface directly, and not the underlying implementation. This is preferred in most cases.

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

  1. By working directly with the interface, you are not acknowledging, and coupling your code to the underlying implementation.
  2. 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.
  3. 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).
  1. 通过直接使用接口,您不会承认并将您的代码耦合到底层实现。
  2. 如果您的代码中已经有一个公共属性 Name 并且您想要实现一个也有 Name 属性的接口,那么明确地执行它将使两者分开。即使他们做同样的事情,我仍然会将显式调用委托给 Name 属性。您永远不知道,您可能想要更改 Name 对普通类的工作方式以及 Name,稍后接口属性的工作方式。
  3. 如果你隐式地实现了一个接口,那么你的类现在公开了可能只与接口的客户端相关的新行为,这意味着你没有保持你的类足够简洁(我认为)。

回答by Bill

If you implement explicitly, you will only be able to reference the interface members through a reference that is of the type of the interface. A reference that is the type of the implementing class will not expose those interface members.

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

If your implementing class is not public, except for the method used to create the class (which could be a factory or IoCcontainer), and except for the interface methods (of course), then I don't see any advantage to explicitly implementing interfaces.

如果您的实现类不是公共的,除了用于创建类的方法(可以是工厂或IoC容器),并且除了接口方法(当然),那么我看不到显式实现的任何优势接口。

Otherwise, explicitly implementing interfaces makes sure that references to your concrete implementing class are not used, allowing you to change that implementation at a later time. "Makes sure", I suppose, is the "advantage". A well-factored implementation can accomplish this without explicit implementation.

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

The disadvantage, in my opinion, is that you will find yourself casting types to/from the interface in the implementation code that does have access to non-public members.

在我看来,缺点是您会发现自己在确实可以访问非公共成员的实现代码中向/从接口转换类型。

Like many things, the advantage is the disadvantage (and vice-versa). Explicitly implementing interfaces will ensure that your concrete class implementation code is not exposed.

像许多事情一样,优点是缺点(反之亦然)。显式实现接口将确保您的具体类实现代码不会暴露。

回答by Matthew Scharley

In addition to excellent answers already provided, there are some cases where explicit implementation is REQUIRED for the compiler to be able to figure out what is required. Take a look at IEnumerable<T>as a prime example that will likely come up fairly often.

除了已经提供的优秀答案之外,在某些情况下,编译器需要显式实现才能确定需要什么。看一下IEnumerable<T>可能会经常出现的主要示例。

Here's an example:

下面是一个例子:

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
}

Here, IEnumerable<string>implements IEnumerable, hence we need to too. But hang on, both the generic and the normal version both implement functions with the same method signature(C# ignores return type for this). This is completely legal and fine. How does the compiler resolve which to use? It forces you to only have, at most, one implicit definition, then it can resolve whatever it needs to.

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

ie.

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: The little piece of indirection in the explicit definition for IEnumerable works because inside the function the compiler knows that the actual type of the variable is a StringList, and that's how it resolves the function call. Nifty little fact for implementing some of the layers of abstraction some of the .NET core interfaces seem to have accumulated.

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

回答by Lee Oades

In addition to the other reasons already stated, this is the situation in which a class is implementing two different interfaces that have a property/method with the same name and signature.

除了已经说明的其他原因之外,这是一个类正在实现两个不同接口的情况,这些接口具有具有相同名称和签名的属性/方法。

/// <summary>
/// This is a Book
/// </summary>
interface IBook
{
    string Title { get; }
    string ISBN { get; }
}

/// <summary>
/// This is a Person
/// </summary>
interface IPerson
{
    string Title { get; }
    string Forename { get; }
    string Surname { get; }
}

/// <summary>
/// This is some freaky book-person.
/// </summary>
class Class1 : IBook, IPerson
{
    /// <summary>
    /// This method is shared by both Book and Person
    /// </summary>
    public string Title
    {
        get
        {
            string personTitle = "Mr";
            string bookTitle = "The Hitchhikers Guide to the Galaxy";

            // What do we do here?
            return null;
        }
    }

    #region IPerson Members

    public string Forename
    {
        get { return "Lee"; }
    }

    public string Surname
    {
        get { return "Oades"; }
    }

    #endregion

    #region IBook Members

    public string ISBN
    {
        get { return "1-904048-46-3"; }
    }

    #endregion
}

This code compiles and runs OK, but the Title property is shared.

此代码编译并运行正常,但 Title 属性是共享的。

Clearly, we'd want the value of Title returned to depend on whether we were treating Class1 as a Book or a Person. This is when we can use the explicit interface.

显然,我们希望返回的 Title 值取决于我们将 Class1 视为 Book 还是 Person。这是我们可以使用显式接口的时候。

string IBook.Title
{
    get
    {
        return "The Hitchhikers Guide to the Galaxy";
    }
}

string IPerson.Title
{
    get
    {
        return "Mr";
    }
}

public string Title
{
    get { return "Still shared"; }
}

Notice that the explicit interface definitions are inferred to be Public - and hence you can't declare them to be public (or otherwise) explicitly.

请注意,显式接口定义被推断为公共 - 因此您不能显式声明它们为公共(或以其他方式)。

Note also that you can still have a "shared" version (as shown above), but whilst this is possible, the existence of such a property is questionable. Perhaps it could be used as a default implementation of Title - so that existing code would not have to be modified to cast Class1 to IBook or IPerson.

另请注意,您仍然可以拥有“共享”版本(如上所示),尽管这是可能的,但此类属性的存在值得怀疑。也许它可以用作 Title 的默认实现 - 这样就不必修改现有代码来将 Class1 转换为 IBook 或 IPerson。

If you do not define the "shared" (implicit) Title, consumers of Class1 mustexplicitly cast instances of Class1 to IBook or IPerson first - otherwise the code will not compile.

如果您没有定义“共享”(隐式)标题,则 Class1 的使用者必须首先将 Class1 的实例显式转换为 IBook 或 IPerson - 否则代码将无法编译。

回答by Jon Nadal

Reason #1

原因#1

I tend to use explicit interface implementation when I want to discourage "programming to an implementation" (Design Principles from Design Patterns).

当我想阻止“编程到实现”(来自设计模式的设计原则)时,我倾向于使用显式接口实现。

For example, in an MVP-based web application:

例如,在基于MVP的 Web 应用程序中:

public interface INavigator {
    void Redirect(string url);
}

public sealed class StandardNavigator : INavigator {
    void INavigator.Redirect(string url) {
        Response.Redirect(url);
    }
}

Now another class (such as a presenter) is less likely to depend on the StandardNavigator implementation and more likely to depend on the INavigator interface (since the implementation would need to be cast to an interface to make use of the Redirect method).

现在另一个类(例如Presenter)不太可能依赖于 StandardNavigator 实现,而更有可能依赖于 INavigator 接口(因为需要将实现转换为接口才能使用 Redirect 方法)。

Reason #2

原因#2

Another reason I might go with an explicit interface implementation would be to keep a class's "default" interface cleaner. For example, if I were developing an ASP.NETserver control, I might want two interfaces:

我可能会使用显式接口实现的另一个原因是保持类的“默认”接口更干净。例如,如果我正在开发一个ASP.NET服务器控件,我可能需要两个接口:

  1. The class's primary interface, which is used by web page developers; and
  2. A "hidden" interface used by the presenter that I develop to handle the control's logic
  1. 类的主要接口,供网页开发人员使用;和
  2. 我开发的用于处理控件逻辑的演示者使用的“隐藏”界面

A simple example follows. It's a combo box control that lists customers. In this example, the web page developer isn't interested in populating the list; instead, they just want to be able to select a customer by GUID or to obtain the selected customer's GUID. A presenter would populate the box on the first page load, and this presenter is encapsulated by the control.

下面是一个简单的例子。这是一个列出客户的组合框控件。在这个例子中,网页开发者对填充列表不感兴趣;相反,他们只想能够通过 GUID 选择客户或获取所选客户的 GUID。演示者将在第一页加载时填充该框,并且该演示者由控件封装。

public sealed class CustomerComboBox : ComboBox, ICustomerComboBox {
    private readonly CustomerComboBoxPresenter presenter;

    public CustomerComboBox() {
        presenter = new CustomerComboBoxPresenter(this);
    }

    protected override void OnLoad() {
        if (!Page.IsPostBack) presenter.HandleFirstLoad();
    }

    // Primary interface used by web page developers
    public Guid ClientId {
        get { return new Guid(SelectedItem.Value); }
        set { SelectedItem.Value = value.ToString(); }
    }

    // "Hidden" interface used by presenter
    IEnumerable<CustomerDto> ICustomerComboBox.DataSource { set; }
}

The presenter populates the data source, and the web page developer never needs to be aware of its existence.

演示者填充数据源,网页开发人员永远不需要知道它的存在。

But's It's Not a Silver Cannonball

但这不是银色炮弹

I wouldn't recommend always employing explicit interface implementations. Those are just two examples where they might be helpful.

我不建议总是使用显式接口实现。这些只是两个可能有用的例子。

回答by Valentin Kuzub

To quote Jeffrey Richter from CLR via C#
(EIMImeans Explicit Interface Method Implementation)

要通过C#CLR从引用杰弗里里希特
EIMI意味着Ëxplicit覆盖整个院落中号ethodmplementation)

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 IntelliSensesupport.
  • Value type instances are boxed when cast to an interface.
  • An EIMI cannot be called by a derived type.

了解使用 EIMI 时存在的一些后果对您来说至关重要。由于这些后果,您应该尽量避免 EIMI。幸运的是,通用接口可以帮助您避免 EIMI。但是可能仍有一些时候您需要使用它们(例如实现两个具有相同名称和签名的接口方法)。以下是 EIMI 的大问题:

  • 没有解释类型如何具体实现 EIMI 方法的文档,并且没有 Microsoft Visual Studio IntelliSense支持。
  • 值类型实例在转换为接口时被装箱。
  • 派生类型不能调用 EIMI。

If you use an interface reference ANY virtual chain can be explicitly replaced with EIMI on any derived class and when an object of such type is cast to the interface, your virtual chain is ignored and the explicit implementation is called. That's anything but polymorphism.

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

EIMIs can also be used to hide non-strongly typed interface members from basic Framework Interfaces' implementations such as IEnumerable<T> so your class doesn't expose a non strongly typed method directly, but is syntactical correct.

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

回答by Yochai Timmer

An implicit interface implementation is where you have a method with the same signature of the interface.

隐式接口实现是您拥有具有相同接口签名的方法的地方。

An explicit interface implementation is where you explicitly declare which interface the method belongs to.

显式接口实现是您显式声明方法属于哪个接口的地方。

interface I1
{
    void implicitExample();
}

interface I2
{
    void explicitExample();
}


class C : I1, I2
{
    void implicitExample()
    {
        Console.WriteLine("I1.implicitExample()");
    }


    void I2.explicitExample()
    {
        Console.WriteLine("I2.explicitExample()");
    }
}

MSDN: implicit and explicit interface implementations

MSDN:隐式和显式接口实现

回答by nrodic

One important use of explicit interface implementation is when in need to implement interfaces with mixed visibility.

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

The problem and solution are well explained in the article C# Internal Interface.

问题和解决方案在文章C# Internal Interface 中得到了很好的解释。

For example, if you want to protect leakage of objects between application layers, this technique allows you to specify different visibility of members that could cause the leakage.

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

回答by supercat

Every class member that implements an interface exports a declaration which is semantically similar to the way VB.NET interface declarations are written, e.g.

每个实现接口的类成员都导出一个声明,该声明在语义上类似于编写 VB.NET 接口声明的方式,例如

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

Although the name of the class member will often match that of the interface member, and the class member will often be public, neither of those things is required. One may also declare:

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

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 specifiedbehavior on all implementations, but usefulbehavior on only some [e.g. the specified behavior for a read-only collection's IList<T>.Addmethod is to throw NotSupportedException]. Unfortunately, the only proper way to implement the interface in C# is:

在这种情况下,将允许类及其派生类使用名称访问类成员IFoo_Foo,但外部世界只能通过强制转换为 来访问该特定成员IFoo。这种方法是在接口方法将有案件往往好规定在所有的实现行为,但有用的只有一些行为[例如指定为只读集合的行为IList<T>.Add方式是抛出NotSupportedException。不幸的是,在 C# 中实现接口的唯一正确方法是:

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

Not as nice.

没那么好。