Answers:
如果您使用相同的方法和不同的实现来实现两个接口,则必须显式实现。
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
超载,以免给人以为您可以比较不同类型的对象。同样,某些接口(例如)也不符合CLS IConvertible
,因此,如果您未明确实现该接口,则需要CLS符合性的语言的最终用户将无法使用您的对象。(如果BCL实现者没有隐藏原语的IConvertible成员,那将是非常灾难性的事情:)
另一个有趣的注释是,通常使用这样的构造意味着显式实现接口的构造只能通过装箱到接口类型来调用它们。您可以通过使用通用约束来解决此问题:
void SomeMethod<T>(T obj) where T:IConvertible
将int传递给int时,不会将其装箱。
string
在非专利药问世之前就已经流行了,这种做法很流行。当.net 2出现时,他们不想破坏.net 2的公共界面,string
因此他们将其保留下来,并保留了适当的防护措施。
明确实现接口的一些其他原因:
向后兼容:万一ICloneable
接口发生变化,实现方法类成员不必更改其方法签名。
更清晰的代码:如果将Clone
方法从ICloneable中删除,则会出现编译器错误,但是,如果隐式实现该方法,则可能会得到未使用的“孤立”公共方法
强类型化:为了举例说明超级猫的故事,这将是我的首选示例代码,当您直接将其作为实例成员调用时,ICloneable
显式实现允许Clone()
强类型化MyObject
:
public class MyObject : ICloneable
{
public MyObject Clone()
{
// my cloning logic;
}
object ICloneable.Clone()
{
return this.Clone();
}
}
interface ICloneable<out T> { T Clone(); T self {get;} }
。请注意,故意没有ICloneable<T>
对T的限制。虽然通常只能在对象的基础可以安全地克隆对象的情况下,但可能希望从可以安全地克隆不能对象的对象的基类派生。为此,我建议不要让可继承的类公开公共克隆方法。而是具有带有protected
克隆方法的可继承类和从它们派生并公开公共克隆的密封类。
另一种有用的技术是让函数的方法的公共实现返回比接口指定的值更具体的值。
例如,一个对象可以实现ICloneable
,但仍然具有其公共可见的Clone
方法返回其自己的类型。
同样,IAutomobileFactory
可能有一个Manufacture
方法返回a Automobile
,而一个FordExplorerFactory
实现IAutomobileFactory
了的Manufacture
方法可能有一个方法返回a FordExplorer
(从派生Automobile
)。知道它对a FordExplorerFactory
所FordExplorer
返回的对象具有可以使用特定属性的FordExplorerFactory
代码,而不必进行类型转换,而只知道它具有某种类型的代码IAutomobileFactory
将简单地将其返回处理为Automobile
。
当您有两个具有相同成员名称和签名的接口,但要根据其使用方式更改其行为时,它也很有用。(我不建议编写这样的代码):
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
}
public class Animal : Cat, Dog
它可以使公共接口更清洁以显式实现接口,即您的File
类可以IDisposable
显式实现并提供公共方法Close()
,该方法对使用者比(Dispose(
)更有意义。
F#仅提供显式接口实现,因此您始终必须强制转换为特定接口才能访问其功能,这使得该接口非常显式(无双关)。
Dispose
是永远不需要清除的内容);更好的示例是类似的不可变集合的实现IList<T>.Add
。
明确实现的另一个原因是可维护性。
当一个班级变得“忙碌”时(是的,是的,我们所有人都不具备重构其他团队成员代码的能力),然后通过一个显式的实现就清楚地知道那里有一种方法可以满足接口协定。
因此,它提高了代码的“可读性”。
#region
,并带有适当的标题字符串。并对方法进行评论。
作者提供了一个不同的示例System.Collections.Immutable
,其中作者选择使用该技术为集合类型保留一个熟悉的API,同时删除接口中对于其新类型没有意义的部分。
具体地,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();
在显式定义的接口的情况下,所有方法都是自动私有的,您不能为它们提供访问修饰符。假设:
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
这就是我们创建显式接口的方式: 如果我们有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();
}
}
}