为什么明确实现接口?
那么,明确实现一个接口的好用例究竟是什么?
仅仅是为了让使用这个类的人不必在intellisense中看到所有这些方法/属性呢?
如果你实现了两个接口,都使用相同的方法和不同的实现,那么你必须明确地实现。
public interface IDoItFast { void Go(); } public interface IDoItSlow { void Go(); } public class JustDoIt : IDoItFast, IDoItSlow { void IDoItFast.Go() { } void IDoItSlow.Go() { } }
隐藏非首选成员是有用的。 例如,如果您同时实现了IComparable<T>
和IComparable
,通常会更好地隐藏IComparable
重载,从而不会让人觉得可以比较不同types的对象。 同样,某些接口不符合CLS,如IConvertible
,所以如果您没有明确实现接口,那么需要符合CLS的语言的最终用户不能使用您的对象。 (如果BCL实施者没有隐藏原语的IConvertible成员,这将是非常灾难性的)
另一个值得注意的是,通常使用这样的构造意味着显式实现接口的结构只能通过装入接口types来调用它们。 你可以通过使用generics约束来解决这个问题::
void SomeMethod<T>(T obj) where T:IConvertible
当你传递一个int的时候不会包含int。
明确实现接口的一些其他原因:
向后兼容性 :如果ICloneable
接口改变,实现方法类成员不必改变他们的方法签名。
更干净的代码 :如果从ICloneable中删除了Clone
方法,将会出现编译器错误,但是如果隐式实现该方法,则最终可能会使用未使用的“孤立”公共方法
强types :用一个例子来说明supercat的故事,这将是我的首选示例代码,当直接调用它作为MyObject
实例成员时,实现ICloneable
显式允许强types化Clone()
:
public class MyObject : ICloneable { public MyObject Clone() { // my cloning logic; } object ICloneable.Clone() { return this.Clone(); } }
另一个有用的技术是让一个方法的函数的公共实现返回一个比接口中指定的更具体的值。
例如,一个对象可以实现ICloneable,但仍然有公开可见的Clone方法返回它自己的types。
同样,IAutomobileFactory可能有一个返回Automobile的Manufacture方法,但实现IAutomobileFactory的FordExplorerFactory可能会使其制造方法返回一个FordExplorer(源自Automobile)。 知道它具有FordExplorerFactory的代码可以使用FordExplorerFactory返回的对象上的特定于FordExplorer的属性,而不必强制转换,而只知道它具有某种types的IAutomobileFactory的代码只会将其返回为汽车。
当你有两个具有相同成员名称和签名的接口,但是想根据使用方式改变它的行为时,它也很有用。 (我不build议这样写代码):
interface Cat { string Name {get;} } interface Dog { string Name{get;} } public class Animal : Cat, Dog { string Cat.Name { get { return "Cat"; } } string Dog.Name { get { return "Dog"; } } } static void Main(string[] args) { Animal animal = new Animal(); Cat cat = animal; //Note the use of the same instance of Animal. All we are doing is picking which interface implementation we want to use. Dog dog = animal; Console.WriteLine(cat.Name); //Prints Cat Console.WriteLine(dog.Name); //Prints Dog }
如果你有一个内部接口,并且你不想公开实现你的类的成员,你可以明确地实现它们。 隐式实现需要公开。
它可以使公共接口清洁器明确地实现一个接口,也就是说,你的File
类可以明确地实现IDisposable
并提供一个公共方法Close()
,这比Dispose(
)更有意义。
F# 只提供了明确的接口实现,所以你总是必须投到特定的接口来访问它的function,这使得接口的使用非常明确(没有双关语意思)。
明确实施的另一个原因是可维护性 。
当一个class级变得“忙碌”时 – 是的,我们并不都有重构其他团队成员代码的奢侈 – 然后有一个明确的实现清楚地表明一种方法在那里满足一个接口契约。
所以它改善了代码的“可读性”。
在明确定义的接口的情况下,所有的方法都是自动私人的,你不能把访问修饰符公开给他们。 假设:
interface Iphone{ void Money(); } interface Ipen{ void Price(); } class Demo : Iphone, Ipen{ void Iphone.Money(){ //it is private you can't give public Console.WriteLine("You have no money"); } void Ipen.Price(){ //it is private you can't give public Console.WriteLine("You have to paid 3$"); } } // So you have to cast to call the method class Program { static void Main(string[] args) { Demo d = new Demo(); Iphone i1 = (Iphone)d; i1.Money(); ((Ipen)i1).Price(); Console.ReadKey(); } } // You can't call methods by direct class object
这是我们如何创build显式接口:如果我们有两个接口,并且接口有相同的方法,并且一个类inheritance这两个接口,所以当我们调用一个接口方法时,编译器会混淆调用哪个方法,所以我们可以使用显式接口来pipe理这个问题。 以下是我在下面给出的一个例子。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace oops3 { interface I5 { void getdata(); } interface I6 { void getdata(); } class MyClass:I5,I6 { void I5.getdata() { Console.WriteLine("I5 getdata called"); } void I6.getdata() { Console.WriteLine("I6 getdata called"); } static void Main(string[] args) { MyClass obj = new MyClass(); ((I5)obj).getdata(); Console.ReadLine(); } } }
System.Collections.Immutable
给出了一个不同的例子,其中作者select使用这种技术来保留一个熟悉的APItypes,同时刮掉那些对于新types没有意义的接口部分。
具体来说, ImmutableList<T>
实现了IList<T>
,因此实现了ICollection<T>
( 为了让ImmutableList<T>
更容易与遗留代码一起使用),但是void ICollection<T>.Add(T item)
感知一个ImmutableList<T>
:因为向一个不可变列表添加一个元素不能改变现有列表, ImmutableList<T>
也从IImmutableList<T>
派生, IImmutableList<T> Add(T item)
可以用于不可变列表。
因此,在Add
的情况下, ImmutableList<T>
的实现最终看起来如下所示:
public ImmutableList<T> Add(T item) { // Create a new list with the added item } IImmutableList<T> IImmutableList<T>.Add(T value) => this.Add(value); void ICollection<T>.Add(T item) => throw new NotSupportedException(); int IList.Add(object value) => throw new NotSupportedException();