为什么要显式地实现接口?

那么,什么才是显式实现接口的好用例呢?

只是为了让使用该类的人不必在智能意义上查看所有这些方法/属性吗?

32687 次浏览

如果您实现了两个接口,它们具有相同的方法和不同的实现,那么您必须显式地实现。

public interface IDoItFast
{
void Go();
}
public interface IDoItSlow
{
void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
void IDoItFast.Go()
{
}


void IDoItSlow.Go()
{
}
}

当您有两个具有相同成员名称和签名的接口,但希望根据使用方式更改其行为时,它也很有用。(我不建议编写这样的代码) :

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
}

If you have an internal interface and you don't want to implement the members on your class publicly, you would implement them explicitly. Implicit implementations are required to be public.

显式实现接口可以保持公共接口的整洁,也就是说,您的 File类可以显式实现 IDisposable,并提供一个公共方法 Close(),这对使用者来说可能比 Dispose(更有意义)。

F # 只有提供了显式的接口实现,因此您总是必须强制转换到特定的接口来访问它的功能,这使得接口的使用非常显式(没有双关语的意思)。

隐藏非首选成员很有用。例如,如果您同时实现了 IComparable<T>IComparable,那么隐藏 IComparable重载通常会更好,这样就不会给人留下可以比较不同类型对象的印象。类似地,有些接口不符合 CLS,比如 IConvertible,所以如果您不显式地实现该接口,那么需要符合 CLS 的语言的最终用户就不能使用您的对象。(如果 BCL 实现者没有隐藏原语的 IConvertable 成员,这将是非常灾难性的:)

另一个有趣的注意事项是,通常使用这样的构造意味着显式实现接口的结构只能通过装箱到接口类型来调用它们。您可以通过使用通用约束来解决这个问题:

void SomeMethod<T>(T obj) where T:IConvertible

传递整型时不会对其进行装箱。

另一个有用的技术是让函数的方法公共实现返回一个比接口中指定的更具体的值。

例如,一个对象可以实现 ICloneable,但是它的公开可见的 Clone方法仍然返回它自己的类型。

Likewise, an IAutomobileFactory might have a Manufacture method which returns an Automobile, but a FordExplorerFactory, which implements IAutomobileFactory, might have its Manufacture method return a FordExplorer (which derives from Automobile). Code which knows that it has a FordExplorerFactory could use FordExplorer-specific properties on an object returned by a FordExplorerFactory without having to typecast, while code which merely knew that it had some type of IAutomobileFactory would simply deal with its return as an Automobile.

显式实现接口的一些其他原因:

向后兼容性 : 在 ICloneable接口更改的情况下,实现方法类成员不必更改其方法签名。

更干净的代码 : 如果从 ICloneable 中删除了 Clone方法,那么将会出现编译器错误,但是如果隐式地实现了该方法,那么最终可能会得到未使用的“孤立”公共方法

强打字 : 为了用一个例子来说明 supercat 的故事,这将是我首选的示例代码,实现 ICloneable显式地允许 Clone()在作为 MyObject实例成员直接调用时被强类型化:

public class MyObject : ICloneable
{
public MyObject Clone()
{
// my cloning logic;
}


object ICloneable.Clone()
{
return this.Clone();
}
}

显式实现的另一个原因是 可维护性

当一个类变得“繁忙”时——是的,这种情况时有发生,我们并不都拥有重构其他团队成员代码的奢侈品——那么拥有一个显式的实现就可以清楚地表明一个方法是用来满足接口契约的。

因此,它提高了代码的“可读性”。

这就是我们如何创建显式接口: 如果我们有2个接口,并且两个接口都有相同的方法,并且一个类继承了这2个接口,所以当我们调用一个接口方法时,编译器会混淆要调用哪个方法,所以我们可以使用显式接口来处理这个问题。 下面是我给出的一个例子。

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();
}
}
}

对于显式定义的接口,所有的方法都是自动私有的,你不能给它们公共的访问修饰符。假设:

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

System.Collections.Immutable给出了一个不同的例子,在这个例子中,作者选择使用这种技术来为集合类型保留一个熟悉的 API,同时去除对其新类型没有任何意义的接口部分。

具体来说,ImmutableList<T>实现了 IList<T>,因此实现了 ICollection<T>(IList<T>0允许 ImmutableList<T>更容易地与遗留代码一起使用) ,但是 void ICollection<T>.Add(T item)ImmutableList<T>没有任何意义: 因为向不可变列表中添加一个元素不能改变现有列表,所以 ImmutableList<T>也是从 IList<T>1派生出来的,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();