在c#中隐式和显式实现接口有什么不同?
什么时候用隐式,什么时候用显式?
这两者之间有什么利弊吗?
微软的官方指南(来自第一版框架设计指南)指出,不建议使用显式实现,因为它会给代码带来意想不到的行为。
我认为这个准则在前ioc时代是非常有效的,当你不把东西作为接口传递的时候。
有人能谈谈这方面的问题吗?
在c#中隐式和显式实现接口有什么不同?
什么时候用隐式,什么时候用显式?
这两者之间有什么利弊吗?
微软的官方指南(来自第一版框架设计指南)指出,不建议使用显式实现,因为它会给代码带来意想不到的行为。
我认为这个准则在前ioc时代是非常有效的,当你不把东西作为接口传递的时候。
有人能谈谈这方面的问题吗?
当前回答
隐式是指通过类上的成员定义接口。显式是指在接口上的类中定义方法。我知道这听起来令人困惑,但我的意思是:IList。CopyTo将隐式实现为:
public void CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
并明确为:
void ICollection.CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
不同之处在于,隐式实现允许您通过创建的类访问接口,方法是将接口转换为该类和接口本身。显式实现允许您仅通过将接口转换为接口本身来访问接口。
MyClass myClass = new MyClass(); // Declared as concrete class
myclass.CopyTo //invalid with explicit
((IList)myClass).CopyTo //valid with explicit.
我使用显式主要是为了保持实现的简洁,或者当我需要两个实现时。不管怎样,我很少使用它。
我相信有更多的理由使用/不使用explicit,其他人会发布。
请参阅本帖的下一篇文章,了解每一篇文章背后的优秀推理。
其他回答
隐式是指通过类上的成员定义接口。显式是指在接口上的类中定义方法。我知道这听起来令人困惑,但我的意思是:IList。CopyTo将隐式实现为:
public void CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
并明确为:
void ICollection.CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
不同之处在于,隐式实现允许您通过创建的类访问接口,方法是将接口转换为该类和接口本身。显式实现允许您仅通过将接口转换为接口本身来访问接口。
MyClass myClass = new MyClass(); // Declared as concrete class
myclass.CopyTo //invalid with explicit
((IList)myClass).CopyTo //valid with explicit.
我使用显式主要是为了保持实现的简洁,或者当我需要两个实现时。不管怎样,我很少使用它。
我相信有更多的理由使用/不使用explicit,其他人会发布。
请参阅本帖的下一篇文章,了解每一篇文章背后的优秀推理。
隐式定义是将接口需要的方法/属性等直接作为公共方法添加到类中。
显式定义强制只在直接使用接口而不是底层实现时才公开成员。在大多数情况下,这是首选。
By working directly with the interface, you are not acknowledging, and coupling your code to the underlying implementation. In the event that you already have, say, a public property Name in your code and you want to implement an interface that also has a Name property, doing it explicitly will keep the two separate. Even if they were doing the same thing I'd still delegate the explicit call to the Name property. You never know, you may want to change how Name works for the normal class and how Name, the interface property works later on. If you implement an interface implicitly then your class now exposes new behaviours that might only be relevant to a client of the interface and it means you aren't keeping your classes succinct enough (my opinion).
显式接口实现的一个重要用途是在需要实现具有混合可见性的接口时。
这个问题和解决方案在c#内部接口这篇文章中有很好的解释。
例如,如果您希望保护应用程序层之间对象的泄漏,该技术允许您指定可能导致泄漏的成员的不同可见性。
除了前面提到的其他原因外,这是指一个类实现了两个不同的接口,而这两个接口的属性/方法具有相同的名称和签名。
/// <summary>
/// This is a Book
/// </summary>
interface IBook
{
string Title { get; }
string ISBN { get; }
}
/// <summary>
/// This is a Person
/// </summary>
interface IPerson
{
string Title { get; }
string Forename { get; }
string Surname { get; }
}
/// <summary>
/// This is some freaky book-person.
/// </summary>
class Class1 : IBook, IPerson
{
/// <summary>
/// This method is shared by both Book and Person
/// </summary>
public string Title
{
get
{
string personTitle = "Mr";
string bookTitle = "The Hitchhikers Guide to the Galaxy";
// What do we do here?
return null;
}
}
#region IPerson Members
public string Forename
{
get { return "Lee"; }
}
public string Surname
{
get { return "Oades"; }
}
#endregion
#region IBook Members
public string ISBN
{
get { return "1-904048-46-3"; }
}
#endregion
}
这段代码编译和运行正常,但是Title属性是共享的。
显然,我们希望返回Title的值取决于我们是将Class1作为Book还是Person来处理。这时我们可以使用显式接口。
string IBook.Title
{
get
{
return "The Hitchhikers Guide to the Galaxy";
}
}
string IPerson.Title
{
get
{
return "Mr";
}
}
public string Title
{
get { return "Still shared"; }
}
注意,显式接口定义被推断为Public -因此您不能显式地将它们声明为Public(或其他)。
还要注意,您仍然可以拥有一个“共享”版本(如上所示),但虽然这是可能的,但这样一个属性的存在是值得怀疑的。也许它可以用作Title的默认实现——这样就不必修改现有的代码来强制转换Class1到IBook或IPerson。
如果你没有定义“共享的”(隐式的)Title, Class1的消费者必须先显式地将Class1的实例转换为IBook或IPerson——否则代码将无法编译。
前面的回答解释了为什么用c#显式地实现接口可能更可取(主要是出于形式上的原因)。然而,有一种情况下显式实现是强制的:为了避免在接口是非公共的,但实现类是公共的情况下泄露封装。
// Given:
internal interface I { void M(); }
// Then explicit implementation correctly observes encapsulation of I:
// Both ((I)CExplicit).M and CExplicit.M are accessible only internally.
public class CExplicit: I { void I.M() { } }
// However, implicit implementation breaks encapsulation of I, because
// ((I)CImplicit).M is only accessible internally, while CImplicit.M is accessible publicly.
public class CImplicit: I { public void M() { } }
上述泄漏是不可避免的,因为根据c#规范,“所有接口成员隐式地具有公共访问权限”。因此,隐式实现也必须提供公共访问,即使接口本身是内部的。
c#中的隐式接口实现非常方便。在实践中,许多程序员一直在使用它,而没有进一步考虑。这在最好的情况下会导致混乱的类型表面,在最坏的情况下会导致泄漏封装。其他语言,比如f#,甚至不允许这样做。