那么,明确实现接口的一个好用例究竟是什么?
仅仅是因为使用该类的人不必查看intellisense中的所有方法/属性吗?
答案 0 :(得分:139)
如果使用相同的方法和不同的实现实现两个接口,则必须明确实现。
public interface IDoItFast
{
void Go();
}
public interface IDoItSlow
{
void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
void IDoItFast.Go()
{
}
void IDoItSlow.Go()
{
}
}
答案 1 :(得分:63)
隐藏非首选成员非常有用。例如,如果同时实现IComparable<T>
和IComparable
,隐藏IComparable
重载通常会更好,不会让人觉得您可以比较不同类型的对象。类似地,某些接口不符合CLS,如IConvertible
,因此如果您没有显式实现接口,那么需要符合CLS的语言的最终用户将无法使用您的对象。 (如果BCL实施者没有隐藏基元的IConvertible成员,那将是非常灾难性的:))
另一个有趣的注意事项是,通常使用这样的构造意味着显式实现接口的struct只能通过装箱接口类型来调用它们。你可以通过使用泛型约束来解决这个问题:
void SomeMethod<T>(T obj) where T:IConvertible
当你传递一个int时,它不会包装一个int。
答案 2 :(得分:36)
明确实现接口的其他一些原因:
向后兼容性:如果ICloneable
接口发生更改,实现方法类成员不必更改其方法签名。
更干净的代码:如果从ICloneable中删除了Clone
方法,则会出现编译器错误,但是如果您隐式实现该方法,则最终可能会使用未使用的“孤立”公共方法
强打字:
为了用一个例子来说明supercat的故事,这将是我首选的示例代码,当你直接将它作为ICloneable
实例成员调用时,实现Clone()
明确允许MyObject
强类型化:
public class MyObject : ICloneable
{
public MyObject Clone()
{
// my cloning logic;
}
object ICloneable.Clone()
{
return this.Clone();
}
}
答案 3 :(得分:12)
另一个有用的技术是让函数的方法的公共实现返回一个比接口中指定的更具体的值。
例如,一个对象可以实现ICloneable
,但仍然可以使用其公开可见的Clone
方法返回自己的类型。
同样,IAutomobileFactory
可能有Manufacture
方法返回Automobile
,但实现FordExplorerFactory
的{{1}}可能有IAutomobileFactory
Manufacture
1}}方法返回FordExplorer
(源自Automobile
)。知道它有FordExplorerFactory
的代码可以使用FordExplorer
- FordExplorerFactory
返回的对象的特定属性,而不必进行类型转换,而代码只知道它有某种类型的{ {1}}只会将其作为IAutomobileFactory
返回。
答案 4 :(得分:7)
当你有两个具有相同成员名称和签名的接口时,它也很有用,但是想要根据它的使用方式改变它的行为。 (我不建议像这样编写代码):
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
}
答案 5 :(得分:5)
如果您有一个内部界面,并且您不想公开在您的类上实现成员,那么您将明确地实现它们。隐式实现必须是公开的。
答案 6 :(得分:5)
它可以使公共接口更清晰,以显式实现接口,即您的File
类可以显式实现IDisposable
并提供一个公共方法Close()
,这可能对消费者更有意义比Dispose(
)。
F# only 提供显式接口实现,因此您必须始终强制转换到特定接口以访问其功能,这使得接口的使用非常明确(无双关语)。
答案 7 :(得分:4)
明确实施的另一个原因是可维护性。
当一个班级忙碌而且#34; - 是的,它发生了,我们都没有重构其他团队成员的奢侈品。代码 - 然后有一个显式的实现清楚地表明一个方法在那里满足接口契约。
因此,它改善了代码的可读性&#34;。
答案 8 :(得分:1)
System.Collections.Immutable
给出了一个不同的例子,其中作者选择使用该技术为集合类型保留熟悉的API,同时刮掉对其新类型没有意义的接口部分。 / p>
具体而言,ImmutableList<T>
实现IList<T>
,因此ICollection<T>
(in order允许ImmutableList<T>
更容易使用遗留代码),但{{1}对于void ICollection<T>.Add(T item)
没有任何意义:因为向不可变列表添加元素不得更改现有列表,ImmutableList<T>
也派生自IImmutableList<T>
,其ImmutableList<T>
可以使用对于不可变列表。
因此,在IImmutableList<T> Add(T item)
的情况下,Add
中的实现最终看起来如下:
ImmutableList<T>
答案 9 :(得分:0)
这就是我们创建Explicit Interface的方法: 如果我们有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();
}
}
}
答案 10 :(得分:0)
如果显式定义了接口,则所有方法都是自动隐私的,您不能将访问修饰符公开给它们。假设:
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