有什么区别在实现接口 隐含地明确 在C#?

当你应该使用隐含和何时应使用明确的?

是否有任何人和/或缺点的一个或其他的?


微软的正式准则(从第一版 框架的设计准则)的国家 使用明确的实现不是建议, ,因为它给人的代码,意想不到的行为。

我认为,这一准则是非常 有效的预IoC-时间, 当你不通过周围的事物作为接口。

任何人都可以接触在这一方面呢?

有帮助吗?

解决方案

隐含是指您通过班级成员定义界面的时间。 显式是指在接口上定义类中的方法。我知道这听起来令人困惑,但这就是我的意思: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来保持实现干净,或者当我需要两个实现时。但无论我很少使用它。

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

请参阅 下一篇文章 在这个帖子中,每个人都有很好的理由。

其他提示

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

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

  1. 通过直接使用界面,您无法确认, 并将您的代码耦合到底层实现。
  2. 如果您已经拥有公共财产名称 你的代码,你想实现一个也有一个 名称属性,明确地将它们保持两个独立。甚至 如果他们做同样的事情,我仍然会委托明确的 调用Name属性。你永远不知道,你可能想要改变 Name如何适用于普通类以及Name如何用于接口 物业以后可以使用。
  3. 如果您隐式实现了一个接口,那么您的类现在会公开 可能只与客户有关的新行为 界面,这意味着你没有保持你的课程简洁 足够(我的意见)。

除了已经提供的优秀答案之外,还有一些情况需要显式实现,以便编译器能够找出所需的内容。看一下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#忽略了返回类型)。这是完全合法和正常的。编译器如何解决使用哪个?它迫使你只有一个隐含的定义,然后它可以解决它需要的任何东西。

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核心接口。

原因#1

当我想阻止<!>编程到实现<!>时,我倾向于使用显式接口实现。 ( 设计模式中的设计原则 )。

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

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

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

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

原因#2

我可能采用显式接口实现的另一个原因是保持一个类的<!> quot; default <!> quot;界面清洁。例如,如果我正在开发 ASP.NET 服务器控件,我可能需要两个接口:

  1. 该类的主要界面,由网页开发人员使用;和
  2. A <!> quot; hidden <!> quot;我开发的用于处理控件逻辑的演示者使用的界面
  3. 下面是一个简单的例子。这是一个列出客户的组合框控件。在此示例中,网页开发人员对填充列表不感兴趣;相反,他们只是希望能够通过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; }
    }
    

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

    但它不是银色炮弹

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

引用杰弗里*里希特,从CLR通过C#
(EIMI 装置 Explicit nterface Method 对执行)

它是极为重要的是你 了解一些影响, 存在时使用EIMIs.因 这些后果,你应该尝试 避免EIMIs尽可能多的。幸运的是,通用的接口,帮助 你避免EIMIs相当多的。但也有 可能还是会时你会需要的 使用它们(例如实施两个 接口的方法与名称相同 和签名)。这里有大 问题EIMIs:

  • 没有文件说明如何具体类型 实现一个EIMI方法,并有 没有Visual Studio IntelliSense 支持。
  • 值的类型实例是盒装的时候投向一个接口。
  • 一个EIMI不能被称为一种源类型。

如果您使用的界面引用任何虚拟链可以是明确代替EIMI上的任何源类和当对象的这种类型的铸造的接口,你的虚拟链是被忽略和明确的执行。这是什么,但多态性。

EIMIs也可以被用来隐藏非强类型接口成员的基本框架的接口'的实现,例如类型<T> 所以你的类不得不强类型的方法是直接的,但是语法正确的。

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

/// <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
}

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

显然,我们希望返回的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"; }
}

请注意,显式接口定义被推断为Public - 因此您无法将它们声明为公开(或其他)显式。

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

如果你没有定义<!> quot; shared <!> quot; (隐式)标题,Class1 的使用者必须首先将Class1的实例显式地转换为IBook或IPerson - 否则代码将无法编译。

我使用明确的界面实现的大部分时间。这里的主要原因。

重构是安全的

当改变一个接口,这是更好的如果编译器可以检查它。这是难隐含的方式实现的。

两个共同的情况下,我想到:

  • 增加一个功能的接口,在现有类实现这种接口已经发生有一个方法,用同样的签名作为新的一个.这可能会导致意想不到的行为,并已被咬我辛苦好几倍。它是困难的"看见"时,因为调试这一功能是不可能位于与其他接口的方法在文件(自的记录问题提到下文)。

  • 去除功能,从一个接口.隐含地实施方法将突然死亡代码,但是明确实施方法会被抓住通过编译错误。即使该死的代码是保持良好,我希望被迫审查和促进它。

这是不幸的,C#没有关键字是力量我们的标记方法作为一个隐含的实施,因此编译可以做的额外检查。虚拟的方法没有任一上述问题由于所需使用的'替代'和'新'。

注:对固定或很少改变的接口(通常从供应商API),这不是一个问题。对于我自己的接口,不过,我不能预测时,他们将如何改变。

这是自的记录

如果我看到'公共bool执行()'的一类,这将需要额外的工作来找出它的部分的一个接口。有人可能会有发表评论,它说所以,或者把它放在一个集团的其他接口实现的,所有下一个区域或分组评论说:"实施工".当然,这只是工作,如果该集团的头是不是屏幕之外..

鉴于:'bool工.执行()'是明确和毫不含糊的。

明确分界面的执行情况

我认为的接口更加"公共"于公共方法,因为它们是造露只是一点点的表面区域的具体类型。他们减少种能力、行为、设置的特性,等等。并在执行情况,我认为这是有用的,以保持这种分离。

因为我看过一类代码,当我遇到过明确的接口实现的,我的大脑转入"代码合同"模式。通常,这些只是实现期待其它方法,但有时他们将做额外的国家/param检查、转换的进入参数,以更好地匹配的内部要求,或者甚至翻译版本控制目的(即多个世代的接口,所有踢下共同实现)。

(我意识到,公众也码的合同,但接口更加强大,尤其是在一个接口驱动代码在哪里的直接使用的具体类型通常是一个标志仅限于内部编码。)

相关: 理由2中所述的乔恩.

等等

加上已经提及的优点在其他的答案在这里:

的问题

这是不是所有的娱乐和幸福。在某些情况下,我坚持隐式转换:

  • 值的类型,因为这将需要拳击和下perf.这不是一个严格的规则,并且取决于接口以及它是如何用来使用。类?隐含的。IFormattable?可能是明确的。
  • 琐碎的系统接口,方法是经常被称为直接(如IDisposable.处置).

此外,它可以是一个痛苦的做铸当你不在的事实有混凝土的类型和要叫一个明确的界面方法。我处理这两种方式之一:

  1. 增加公众和有界面方法向他们为实现。通常会有更简单的接口工作时,内部。
  2. (我的首选方法)添加一个 public IMyInterface I { get { return this; } } (这应该得到内联)和呼叫 foo.I.InterfaceMethod().如果多个接口,需要此能力,扩大名称超出了I(以我的经验这是罕见的,我有这种需要).

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

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

否则,显式实现接口可确保不使用对具体实现类的引用,从而允许您稍后更改该实现。 <!>“;确定<!>”,我想,是<!> quot; advantage <!>“;一个考虑周全的实现可以在没有明确实现的情况下实现这一点。

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

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

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

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

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: 隐式和显式界面实施

每类成员实现一个接口出口的一项宣言,其在语义上是类似的方式VB.NET 接口的声明是书面的,例如

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

虽然名称的类成员经常将相匹配的界面部件和类成员经常将是公开的,既不是这些事情是必需的。一个还可以声明:

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

在这种情况下的类及其衍生物就会被允许访问的一类成员使用的名称 IFoo_Foo, 但外面的世界将会只能够访问特定的部件通过的铸造 IFoo.这种办法往往是良好的情况下,一个接口,方法有 指定 行为在所有实现,但是 有用的 行为只有一些[例如指定的行为只读收集的 IList<T>.Add 方法是要扔 NotSupportedException].不幸的是,只有适当的方式来实现接口在C#是:

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

不不错。

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

问题和解决方案在 C#Internal Interface

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top