那么,实现接口显式地有什么好处呢?
只是为了让使用该类的人不必在智能提示中查看所有这些方法/属性吗?
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
如果你传递一个整数给它,它不会将其装箱。
if (!(value is String)) {
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeString"));
}
return String.Compare(this,(String)value, StringComparison.CurrentCulture);
} 谢谢! - zzfimastring
就已经被广泛使用了。当 .net 2 出现时,他们不想破坏 string
的公共接口,所以他们保留了原样,并加入了保障措施。 - Michael B显式实现接口的一些附加原因:
向后兼容性:如果 ICloneable
接口发生更改,实现方法类成员不必更改其方法签名。
更干净的代码:如果从 ICloneable 中删除 Clone
方法,则会出现编译器错误,但是如果您隐式实现该方法,则可能会出现未使用的“孤立”公共方法。
强类型:
为了说明supercat的故事,这将是我首选的示例代码,显式实现 ICloneable
允许在直接调用MyObject
实例成员时使用强类型的 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
的克隆方法,并且从它们派生的密封类可以公开克隆方法。 - supercat另一种有用的技巧是使函数的公共方法实现返回比接口规定的更具体的值。
例如,一个对象可以实现 ICloneable
接口,但仍然可以让它公开可见的 Clone
方法返回其自身类型。
同样地,一个 IAutomobileFactory
可能会有一个返回 Automobile
的 Manufacture
方法,但实现 IAutomobileFactory
的 FordExplorerFactory
可以让它的 Manufacture
方法返回一个 FordExplorer
(它派生自 Automobile
)。知道自己有一个 FordExplorerFactory
的代码可以使用由 FordExplorerFactory
返回的对象上的 FordExplorer
特定属性,而仅知道自己拥有某种类型的 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
,意思是把Animal
类声明为继承自Cat
和Dog
类。 - mbx明确实现接口可以使公共接口更加清晰,例如您的File
类可能会明确实现IDisposable
并提供一个名为Close()
的公共方法,这对消费者来说可能比Dispose()
更有意义。
F# 仅提供明确实现接口,因此您始终需要将其转换为特定的接口才能访问其功能,这使得接口的使用非常明确。
IList<T>.Add
。 - supercat显式实现的另一个原因是为了可维护性。
当一个类变得“繁忙”时,拥有显式实现可以清楚地表明方法存在于其中以满足接口合同。
因此,它提高了代码的“可读性”。
#region
的作用,需要适当的标题字符串。同时,在方法上添加注释。 - ToolmakerSteveImmutableList<T>
实现了 IList<T>
接口和 ICollection<T>
接口(为了 更方便地与旧代码一起使用),但是对于一个不可变的列表来说,void ICollection<T>.Add(T item)
没有意义:由于向不可变列表添加元素不应更改现有列表,因此 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();
这是我们如何创建显式接口: 如果我们有两个接口,且两个接口都有相同的方法,并且一个类继承了这两个接口,那么当我们调用一个接口方法时,编译器会混淆应该调用哪个方法,因此我们可以使用显式接口来解决这个问题。 下面是一个示例。
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();
}
}
}