【发布时间】:2011-05-05 10:27:18
【问题描述】:
那么,明确实现接口的好用例到底是什么?
是否只是为了让使用该类的人不必查看智能感知中的所有这些方法/属性?
【问题讨论】:
那么,明确实现接口的好用例到底是什么?
是否只是为了让使用该类的人不必查看智能感知中的所有这些方法/属性?
【问题讨论】:
另一个有用的技术是让函数的方法的公共实现返回一个比接口中指定的值更具体的值。
例如,一个对象可以实现ICloneable,但仍然让其公开可见的Clone 方法返回它自己的类型。
同样,IAutomobileFactory 可能有一个返回Automobile 的Manufacture 方法,但实现IAutomobileFactory 的FordExplorerFactory 可能有它的Manufacture 方法返回一个FordExplorer(它派生来自Automobile)。知道它具有FordExplorerFactory 的代码可以在FordExplorerFactory 返回的对象上使用FordExplorer 特定的属性,而无需进行类型转换,而只知道它具有某种类型的代码IAutomobileFactory 将简单地处理它以Automobile 的形式返回。
【讨论】:
System.Collections.Immutable 给出了一个不同的示例,其中作者选择使用该技术为集合类型保留熟悉的 API,同时删除接口中对其新类型没有意义的部分。
具体来说,ImmutableList<T> 实现了IList<T>,因此实现了ICollection<T>(in order 允许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();
【讨论】:
我们可以这样创建显式接口: 如果我们有 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
【讨论】:
显式实现的另一个原因是可维护性。
当一个类变得“忙碌”时——是的,它确实发生了,我们并不都有重构其他团队成员的代码的奢侈——然后有一个显式的实现可以清楚地表明一个方法可以满足一个接口契约。
因此它提高了代码的“可读性”。
【讨论】:
#region 的用途,并带有适当的标题字符串。以及对该方法的评论。
显式实现接口的一些额外原因:
向后兼容性:如果ICloneable 接口发生变化,实现方法类成员不必更改其方法签名。
更简洁的代码:如果从 ICloneable 中删除 Clone 方法,则会出现编译器错误,但是如果您隐式实现该方法,您最终可能会得到未使用的“孤立”公共方法
强类型:
用一个例子来说明 supercat 的故事,这将是我首选的示例代码,当您直接将 Clone() 作为 MyObject 实例成员调用时,显式实现 ICloneable 允许强类型化 Clone():
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;} }。请注意,在 T 上刻意没有ICloneable<T> 约束。虽然一个对象通常只能在其基可以被安全克隆的情况下才能被安全克隆,但人们可能希望从可以安全克隆的类的对象派生“可以”吨。为此,我建议不要让可继承类公开公共克隆方法。而是使用具有protected 克隆方法的可继承类和从它们派生并公开公共克隆的密封类。
隐藏非首选成员很有用。例如,如果你同时实现了IComparable<T> 和IComparable,隐藏IComparable 重载通常会更好,以免给人留下可以比较不同类型对象的印象。同样,某些接口不符合 CLS,例如 IConvertible,因此如果您不明确实现接口,则需要符合 CLS 的语言的最终用户无法使用您的对象。 (如果 BCL 实现者没有隐藏原语的 IConvertible 成员,那将是非常灾难性的 :))
另一个有趣的注意事项是,通常使用这样的构造意味着显式实现接口的结构只能通过装箱到接口类型来调用它们。你可以通过使用通用约束来解决这个问题::
void SomeMethod<T>(T obj) where T:IConvertible
当你将一个 int 传递给它时,它不会装箱。
【讨论】:
string 出现在泛型之前,这种做法很流行。当 .net 2 出现时,他们不想破坏 string 的公共界面,所以他们将其保留在原样,并保留了安全措施。
它可以使公共接口更清晰以显式实现一个接口,即您的File 类可能显式实现IDisposable 并提供一个公共方法Close(),这对消费者来说可能比Dispose( 更有意义)。
F# only 提供显式接口实现,因此您始终必须强制转换为特定接口才能访问其功能,这使得接口的使用非常显式(没有双关语)。
【讨论】:
Dispose 的是那些永远不需要清理的东西);一个更好的例子是 IList<T>.Add 的不可变集合的实现。
如果您有一个内部接口并且您不想公开实现类中的成员,则可以显式实现它们。隐式实现需要公开。
【讨论】:
当您有两个具有相同成员名称和签名的接口,但希望根据其使用方式更改其行为时,它也很有用。 (我不推荐这样写代码):
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
如果你实现了两个接口,都具有相同的方法和不同的实现,那么你必须显式地实现。
public interface IDoItFast
{
void Go();
}
public interface IDoItSlow
{
void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
void IDoItFast.Go()
{
}
void IDoItSlow.Go()
{
}
}
【讨论】: