CSharp - c#接口, 隐式实现和Explicit实现

  显示原文与译文双语对照的内容

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

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

有什么优点和/或者缺点?


微软的官方指南( 从第一版框架设计指南 ) 指出,使用显式的实现都不推荐 ,因为它给出了代码不可预见的情况。

我认为这条原则是非常有效在一个 pre-IoC-time,当你做的这种局面成接口,不能通过的。

还有谁能触摸那个方面?

时间:

当你通过类的成员定义接口时,可以使用英镑的 当你在接口上的类中定义方法时,显式地使用 我知道这听起来很混乱,但这是我的意思: 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.

我主要使用显式来保持实现干净,或者当我需要两个实现时。 但不管我怎么用它。

我相信有更多的理由使用它/不使用它,其他人将发帖。

在这个线程很好的推理each,背后看到 下一篇文章.

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

显式定义强制仅当你直接使用接口而不是底层实现时公开方法。 这在大多数情况下是首选的。

  1. 通过直接使用该接口,你无法确认,并且将你的代码耦合到底层实现。
  2. 如果你已经有了代码中的公共属性名称,并且希望实现一个具有名称属性的接口,那么显式地执行它将保持两个独立的属性。 即使他们做了同样的事情,我仍然将显式调用委托给名称属性。 你永远不知道,你可能想更改普通类的名称以及名称的工作方式,接口属性稍后在。
  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,因此我们需要。 使用相同的方法签名不过请打住,这两个同时。一般与正常版本实现等功能。 这是完全合法和好的。 编译器如何解析要使用的? 它强制你只有一个隐式定义,然后它可以解析它需要的任何东西。

等等


StringList sl = new StringList();

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

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

原因 #1

当我想阻止"编程到实现"( http://www.artima.com/lejava/articles/designprinciples.html ) 时,我倾向于使用显式接口实现。

例如在MVP-based网络应用中


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

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

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

原因 #2

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

  1. 网页开发人员使用的类接口的主接口;
  2. 演示者使用的"隐藏"接口,我开发它来处理控制逻辑

下面是一个简单的示例。它是一个列出客户的组合框控件。 在这个例子中,网页开发人员对填充 List 不感兴趣;相反,他们只是想通过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; } 
}

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

但不是银炮弹

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

从 C# 中引用 Jeffrey 。Richter
( EIMI 意味着 E xplicit nterface M ethod mplementation )

理解使用EIMIs时存在的一些后果是非常重要的。 由于这些后果,你应该尽量避免 EIMIs 。 幸运的是,通用接口可以帮助你很好地避免 EIMIs 。 但是有时候你可能需要使用它们( 如实现具有相同名称和签名的两个接口方法) 。 以下是EIMIs的一些大问题:

  • 没有解释特定类型的文档的文档
    实现一个EIMI方法,并
    不是 Microsoft Visual Studio
    智能感知支持。
  • 值类型实例在转换为接口时被装箱。
  • 派生类型不能调用 EIMI 。

通常我认为接口是半( 充其量) 面向对象特性,它提供继承,但它并不提供真正的多态性。 要获得真实多态,你需要基类引用,它将保证正确使用虚方法链。

如果使用接口显式地引用任何虚拟连锁都可以替换为任何派生类上EIMI和这种类型是强制转换到该接口的对象时,将忽略你的虚拟链和显式实现,称为。 这只是多态性的问题。

EIMIs'从基本框架还可以用来隐藏非强类型接口成员接口实现,诸如重复元素除外那么你的类不直接公开一个非强类型的方法,不过是语法上正确的。

除其他原因外,还说明了类实现 2不同接口的情况,这些接口具有具有相同名称和signiture的属性/方法。


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

这里代码编译并运行ok确定,但标题属性是共享的。

显然,我们希望返回的标题值取决于我们是否将当作一个图书或者一个人。 这是我们使用显式接口的时候。


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

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

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

注意,显式接口定义被推断为公共的,因此你不能将它们声明为公共( 否则否则) 。

注意,你仍然可以有"共享"版本( 如上所示),但这是可能的,这样一个属性的存在是有问题的。 也许它可以作为标题的默认实现,这样就不用修改现有代码来将Class1转换为IBook或者 IPerson 。

如果不定义"共享"( 隐式)的标题,尽可能有效地泄放高能量入地的使用者必须 explictly尽可能有效地泄放高能量入地的实例强制转换为IBook或者IPerson先- 否则代码无法编译。

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

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


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: 隐式和显式接口实现

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

如果实现类不是公共的,除了用于创建类( 可以是工厂或者IOC容器)的方法外,除了接口方法( 当然) 之外,其他方法都是不公开的,因此我没有看到显式实现接口的任何优点。

否则,显式实现接口将确保不使用对具体实现类的引用,允许以后更改该实现。 "确保"我想,这就是"优势"。 well-factored实现无需显式实现即可实现这里操作。

在我看来,缺点是你将发现自己将类型从接口转换为具有访问非公共成员的实现代码的接口。

像许多东西一样,优势是( 和 vice-versa ) 。 显式实现接口将确保不公开具体的类实现代码。

大多数时候都使用显式接口实现。 以下是主要原因。

重构更安全

更改接口时,编译器可以检查它更好。 对于隐式实现来说,这一点比较困难。

有两种常见的情况:

  • 具有相同签名,让新的, 加一个用户函数到一个接口,在这里已经正好可以使用现有的类,该类实现这里接口 method. 这可能导致意外的行为,并多次咬我。 调试时很难"查看",因为该函数可能没有与文件( 下面提到的self-documenting问题) 中的其他接口方法一起定位。

  • 在一个接口, 删除一个函数关系 隐式实现的方法会突然死掉代码,但是显式实现的方法会被编译错误捕获。 即使死代码是好的,我也想被强制检查它并提升它。

不幸的是,C# 没有强制我们将方法标记为隐式实现的关键字,因此编译器可以执行额外的检查。 由于需要使用'重写'和'新建',虚方法没有上述任何问题。

注意:对于固定或者rarely-changing接口,这不是一个问题。 但是,对于我自己的接口,我无法预测什么时候/如何更改。

是 self-documenting

如果我在一个类中看到'公共 bool Execute()',那么需要额外的工作来确定它是接口的一部分。 有人将很可能不得不把它注释说是这样,或者将它放在一组其他的接口实现,在区域或者分组下所有评论说"调试程序的实现"。 当然,只有在组页眉不在屏幕外时,才有效。

然而:'布尔类型。执行( ) 是清晰明确的。

清晰的接口实现

我认为接口比公共方法更为'公用',因为它们被用来只公开具体类型的表面区域。 它们将类型减少到一个功能,一个行为,一组特性,等等 和实现,我认为保持这个分离是有用的。

当我查看一个类的代码时,当我遇到显式接口实现时,我的大脑会变成"代码协定"模式。 通常这些实现只是转发到其他方法,但有时它们会进行额外的状态/参数检查,转换传入参数以更好地匹配内部需求,甚至是转换目的( 例如 ) 。 多世代接口所有punting向下到通用实现。

( 我意识到publics也是代码协定,但接口更强大,尤其是在interface-driven代码库中直接使用具体类型的代码通常是internal-only代码的标志。)

相关︰由 Jon 原因 2以上。

在,

另外,在这里已经提到了其他的优点:

问题

并不是所有的快乐和快乐。 有些情况下我坚持使用 implicits:

  • 值类型,因为需要装箱和降低性能。 这不是一个严格的规则,取决于接口以及它是如何使用的。 IComparable隐式。IFormattable可能是显式的。
  • 具有直接调用( 类似于 idisposable 。dispose )的方法的简单系统接口。

同样,当你确实有具体的类型并且想要调用显式接口方法时,你可能会感到痛苦。 我用以下两种方法来处理:

  1. 添加publics并为实现提供接口方法。 通常在内部工作时使用较简单的接口。
  2. ( 我的首选方法) 添加一个 public IMyInterface I { get { return this; } } ( 应该得到内联) 并调用 foo.I.InterfaceMethod() 。 如果有多个接口需要这里功能,请将名称扩展到( 以我的经验,我很少有这种需求) 之外。

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

问题和解决方案在 C# 内部接口 文章中得到了很好的解释。

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

...