C# 接口。隐式实现与显式实现
在 C# 中隐式和显式实现接口有什么区别?
什么时候应该使用隐式,什么时候应该使用显式?
其中之一有优点和/或缺点吗?
Microsoft 的官方指南(来自第一版框架设计指南)指出使用不建议显式实现,因为它会给代码带来意外的行为。
我认为这个指南在 IoC 之前的时期非常有效,当你不将东西作为接口传递时。
有人也可以谈谈这方面吗?
What are the differences in implementing interfaces implicitly and explicitly in 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.
I think this guideline is very valid in a pre-IoC-time, when you don't pass things around as interfaces.
Could anyone touch on that aspect as well?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
隐式是指您通过类中的成员定义接口。 显式是指您在接口上的类中定义方法。我知道这听起来令人困惑,但这就是我的意思:
IList.CopyTo
将隐式实现为:和显式实现为:
区别在于隐式实现允许您通过您创建的类访问接口将接口强制转换为该类以及接口本身。显式实现允许您仅通过将其转换为接口本身来访问该接口。
我使用显式主要是为了保持实现干净,或者当我需要两个实现时。无论如何,我很少使用它。
我确信其他人会发布更多使用/不使用显式的理由。
请参阅此帖子中的下一篇文章每个背后都有出色的推理。
Implicit is when you define your interface via a member on your class. Explicit is when you define methods within your class on the interface. I know that sounds confusing but here is what I mean:
IList.CopyTo
would be implicitly implemented as:and explicitly as:
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.
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 post in this thread for excellent reasoning behind each.
隐式定义就是将接口所需的方法/属性等直接添加到类中作为公共方法。
显式定义强制仅在您直接使用接口(而不是底层实现)时才公开成员。在大多数情况下这是首选的。
并将您的代码耦合到底层实现。
您的代码并且您想要实现一个也有一个接口
Name 属性,明确地执行此操作将使两者保持分离。甚至
如果他们做同样的事情我仍然会委托明确的
调用 Name 属性。你永远不知道,你可能想要改变
Name 如何用于普通类以及 Name、接口如何工作
物业稍后运作。
可能只与客户相关的新行为
界面,这意味着你没有保持你的类简洁
足够了(我的意见)。
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.
and coupling your code to the underlying implementation.
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.
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).
除了已经提供的出色答案之外,在某些情况下,需要显式实现,编译器才能弄清楚需要什么。看一下
IEnumerable
作为一个可能经常出现的主要示例。这是一个示例:
这里,
IEnumerable
实现了IEnumerable
,因此我们也需要这样做。但请稍等,通用版本和普通版本都都实现具有相同方法签名的函数(C# 会为此忽略返回类型)。这是完全合法且良好的。编译器如何决定使用哪个?它迫使您最多只有一个隐式定义,然后它就可以解决它需要的任何问题。IE。
PS: IEnumerable 显式定义中的一小段间接寻址之所以有效,是因为在函数内部,编译器知道变量的实际类型是 StringList,这就是它解析函数调用的方式。 .NET 核心接口的一些抽象层的实现似乎已经积累了一些有趣的小事实。
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.Here's an example:
Here,
IEnumerable<string>
implementsIEnumerable
, 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.ie.
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.
原因#1
当我想阻止“编程实现”时,我倾向于使用显式接口实现(设计模式的设计原则)。
例如,在基于 MVP 的网络中application:
现在是另一个类(例如 presenter) 不太可能依赖于 StandardNavigator 实现,而更有可能依赖于 INavigator 接口(因为需要将实现转换为接口才能使用 Redirect 方法) 。
原因#2
我可能采用显式接口实现的另一个原因是保持类的“默认”接口更干净。例如,如果我正在开发 ASP.NET 服务器控件,我可能需要两个接口:
下面是一个简单的示例。它是一个列出客户的组合框控件。在此示例中,网页开发人员对填充列表不感兴趣;而是对填充列表感兴趣。相反,他们只是希望能够通过 GUID 选择客户或获取所选客户的 GUID。演示者将在第一页加载时填充该框,并且该演示者由控件封装。
演示者填充数据源,网页开发人员永远不需要知道它的存在。
但这不是银色炮弹,
我不建议总是使用显式接口实现。这些只是可能有帮助的两个例子。
Reason #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:
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).
Reason #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.NET server control, I might want two interfaces:
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.
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.
引用来自 CLR 通过 C# 的 Jeffrey Richter
(EIMI表示E显式I接口M方法I实现)
如果您使用接口引用,则任何虚拟链都可以在任何派生类上显式替换为 EIMI,并且当此类类型的对象转换为接口时,您的虚拟链将被忽略并调用显式实现。这绝不是多态性。
EIMI 还可用于从基本框架接口的实现(例如 IEnumerable)中隐藏非强类型接口成员。因此您的类不会直接公开非强类型方法,但语法正确。
To quote Jeffrey Richter from CLR via C#
(EIMI means Explicit Interface Method Implementation)
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.
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.
我大部分时间都使用显式接口实现。以下是主要原因。
重构更安全
当更改接口时,如果编译器能够检查它,那就更好了。对于隐式实现来说,这更加困难。
我想到了两种常见的情况:
将函数添加到接口,其中实现该接口的现有类恰好具有与新类具有相同签名的方法。这可能会导致意想不到的行为,并且已经让我好几次了。调试时很难“看到”,因为该函数可能未与文件中的其他接口方法一起定位(下面提到的自记录问题)。
从接口中删除函数。隐式实现的方法将突然成为死代码,但显式实现的方法将被编译错误捕获。即使死代码很好保留,我也想被迫对其进行审查并推广它。
不幸的是,C# 没有关键字强制我们将方法标记为隐式实现,因此编译器可以进行额外的检查。由于需要使用“override”和“new”,虚拟方法不存在上述任何一个问题。
注意:对于固定或很少更改的接口(通常来自供应商 API),这不是问题。但对于我自己的界面,我无法预测它们何时/如何改变。
它是自记录的
如果我在类中看到“public bool Execute()”,则需要额外的工作才能确定它是接口的一部分。有人可能不得不这样评论它,或者将它放在一组其他接口实现中,所有这些都在一个区域或分组注释下,并写着“ITask 的实现”。当然,只有当组标题不在屏幕外时才有效。
而:“bool ITask.Execute()”是清晰且明确的。
接口实现的清晰分离
我认为接口比公共方法更“公共”,因为它们被精心设计为仅公开具体类型的一小部分表面区域。他们将类型简化为一种能力、一种行为、一组特征等。在实现中,我认为保持这种分离是有用的。
当我查看类的代码时,当我遇到显式接口实现时,我的大脑就会转变为“代码契约”模式。通常,这些实现只是转发到其他方法,但有时它们会进行额外的状态/参数检查、转换传入参数以更好地匹配内部需求,甚至出于版本控制目的进行翻译(即,多代接口都向下转换为通用实现)。
(我意识到公共也是代码契约,但接口要强大得多,特别是在接口驱动的代码库中,直接使用具体类型通常是仅限内部代码的标志。)
相关:Jon 的上述原因 2。
等等
再加上其他答案中已经提到的优点:
问题
这并不全是乐趣和幸福。在某些情况下,我坚持使用隐式:
此外,当您实际上拥有具体类型并想要调用显式接口方法时,进行转换可能会很痛苦。我通过以下两种方式之一处理这个问题:
I use explicit interface implementation most of the time. Here are the main reasons.
Refactoring is safer
When changing an interface, it's better if the compiler can check it. This is harder with implicit implementations.
Two common cases come to mind:
Adding a function to an interface, where an existing class that implements this interface already happens to have a method with the same signature as the new one. This can lead to unexpected behavior, and has bitten me hard several times. It's difficult to "see" when debugging because that function is likely not located with the other interface methods in the file (the self-documenting issue mentioned below).
Removing a function from an interface. Implicitly implemented methods will be suddenly dead code, but explicitly implemented methods will get caught by compile error. Even if the dead code is good to keep around, I want to be forced to review it and promote it.
It's unfortunate that C# doesn't have a keyword that forces us to mark a method as an implicit implementation, so the compiler could do the extra checks. Virtual methods don't have either of the above problems due to required use of 'override' and 'new'.
Note: for fixed or rarely-changing interfaces (typically from vendor API's), this is not a problem. For my own interfaces, though, I can't predict when/how they will change.
It's self-documenting
If I see 'public bool Execute()' in a class, it's going to take extra work to figure out that it's part of an interface. Somebody will probably have to comment it saying so, or put it in a group of other interface implementations, all under a region or grouping comment saying "implementation of ITask". Of course, that only works if the group header isn't offscreen..
Whereas: 'bool ITask.Execute()' is clear and unambiguous.
Clear separation of interface implementation
I think of interfaces as being more 'public' than public methods because they are crafted to expose just a bit of the surface area of the concrete type. They reduce the type to a capability, a behavior, a set of traits, etc. And in the implementation, I think it's useful to keep this separation.
As I am looking through a class's code, when I come across explicit interface implementations, my brain shifts into "code contract" mode. Often these implementations simply forward to other methods, but sometimes they will do extra state/param checking, conversion of incoming parameters to better match internal requirements, or even translation for versioning purposes (i.e. multiple generations of interfaces all punting down to common implementations).
(I realize that publics are also code contracts, but interfaces are much stronger, especially in an interface-driven codebase where direct use of concrete types is usually a sign of internal-only code.)
Related: Reason 2 above by Jon.
And so on
Plus the advantages already mentioned in other answers here:
Problems
It's not all fun and happiness. There are some cases where I stick with implicits:
Also, it can be a pain to do the casting when you do in fact have the concrete type and want to call an explicit interface method. I deal with this in one of two ways:
public IMyInterface I { get { return this; } }
(which should get inlined) and callfoo.I.InterfaceMethod()
. If multiple interfaces that need this ability, expand the name beyond I (in my experience it's rare that I have this need).除了已经陈述的其他原因之外,在这种情况下,一个类正在实现两个具有相同名称和签名的属性/方法的不同接口。
此代码编译并运行正常,但 Title 属性是共享的。
显然,我们希望返回的 Title 值取决于我们是将 Class1 视为一本书还是一个人。这时我们就可以使用显式接口了。
请注意,显式接口定义被推断为公共 - 因此您不能将它们显式声明为公共(或其他方式)。
另请注意,您仍然可以拥有“共享”版本(如上所示),但虽然这是可能的,但这种属性的存在是值得怀疑的。也许它可以用作 Title 的默认实现 - 这样就不必修改现有代码即可将 Class1 转换为 IBook 或 IPerson。
如果您没有定义“共享”(隐式)标题,则 Class1 的使用者必须首先将 Class1 的实例显式转换为 IBook 或 IPerson - 否则代码将无法编译。
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.
This code compiles and runs OK, but the Title property is shared.
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.
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.
If you do not define the "shared" (implicit) Title, consumers of Class1 must explicitly cast instances of Class1 to IBook or IPerson first - otherwise the code will not compile.
如果显式实现,则只能通过接口类型的引用来引用接口成员。作为实现类类型的引用不会公开这些接口成员。
如果您的实现类不是公共的,除了用于创建该类的方法(可能是工厂或 IoC 容器),除了接口方法(当然),那么我没有看到显式实现接口的任何优势。
否则,显式实现接口可确保不使用对具体实现类的引用,从而允许您稍后更改该实现。我想,“确保”就是“优势”。精心设计的实现无需显式实现即可实现此目的。
在我看来,缺点是您会发现自己在可以访问非公共成员的实现代码中将类型转换为接口或从接口转换类型。
像许多事情一样,优点就是缺点(反之亦然)。显式实现接口将确保您的具体类实现代码不会暴露。
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 IoC container), and except for the interface methods (of course), then I don't see any advantage to explicitly implementing interfaces.
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.
每个实现接口的类成员都会导出一个声明,该声明在语义上与编写 VB.NET 接口声明的方式类似,例如,
尽管类成员的名称通常与接口成员的名称相匹配,并且类成员通常是公共的,这些都不是必需的。还可以声明:
在这种情况下,类及其派生类将被允许使用名称
IFoo_Foo
访问类成员,但外部世界只能通过强制转换为 <代码>IFoo。当接口方法在所有实现上都具有指定行为,但仅在某些实现上有用行为时,这种方法通常是很好的[例如,只读的指定行为]集合的IList.Add
方法是抛出NotSupportedException
]。不幸的是,在 C# 中实现该接口的唯一正确方法是:不太好。
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.
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:
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 toIFoo
. 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'sIList<T>.Add
method is to throwNotSupportedException
]. Unfortunately, the only proper way to implement the interface in C# is:Not as nice.
隐式接口实现是指具有与接口相同签名的方法。
显式接口实现是显式声明该方法属于哪个接口的地方。
MSDN:隐式和显式接口实现
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.
MSDN: implicit and explicit interface implementations
前面的答案解释了为什么在 C# 中显式实现接口可能是更好的选择(主要出于正式原因)。然而,有一种情况强制显式实现:当接口是非
public
但实现类是public时,为了避免泄漏封装
。上述泄漏是不可避免的,因为根据 C# 规范,“所有接口成员都隐式具有公共访问权限。”因此,隐式实现也必须提供
公共
访问权限,即使接口本身是内部
。C# 中的隐式接口实现非常方便。在实践中,许多程序员一直/到处都在使用它,而没有进一步考虑。这最多会导致混乱的类型表面,最坏的情况会导致封装泄漏。其他语言,例如 F#,甚至不允许。
The previous answers explain why implementing an interface explicitly in C# may be preferrable (for mostly formal reasons). However, there is one situation where explicit implementation is mandatory: In order to avoid leaking the encapsulation when the interface is non-
public
, but the implementing class ispublic
.The above leakage is unavoidable because, according to the C# specification, "All interface members implicitly have public access." As a consequence, implicit implementations must also give
public
access, even if the interface itself is e.g.internal
.Implicit interface implementation in C# is a great convenience. In practice, many programmers use it all the time/everywhere without further consideration. This leads to messy type surfaces at best and leaked encapsulation at worst. Other languages, such as F#, don't even allow it.
显式接口实现的一个重要用途是当需要实现具有混合可见性的接口时。
问题和解决方案在文章 C# 内部接口。
例如,如果您想保护应用程序层之间的对象泄漏,此技术允许您指定可能导致泄漏的成员的不同可见性。
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.
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.
我发现自己最近更频繁地使用显式实现,原因如下实用:
从一开始就始终使用显式实现可以防止出现任何命名冲突,其中显式实现无论如何,都需要实施
消费者“被迫”使用接口而不是实现(又名不是”编程实现”),当你使用 DI 时,他们应该/必须这样做
实现中没有“僵尸”成员 - 从接口声明中删除任何成员如果不从实现中删除,将导致编译器错误
自动采用可选参数的默认值以及通用参数的约束 - 无需写两次并保持同步
I've found myself using explicit implementations more often recently, for the following practical reasons:
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