mat*_*ant 472
隐含的是您通过班级中的成员定义界面时.显式是在界面上定义类中的方法时.我知道这听起来令人困惑,但这就是我的意思:IList.CopyTo将隐含地实现为:
public void CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
Run Code Online (Sandbox Code Playgroud)
并明确地表示为:
void ICollection.CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
Run Code Online (Sandbox Code Playgroud)
不同之处在于隐式地可以通过您在作为该类进行转换时创建的类以及将其作为接口进行转换来访问.显式实现允许它仅在作为接口本身进行转换时可访问.
MyClass myClass = new MyClass(); // Declared as concrete class
myclass.CopyTo //invalid with explicit
((IList)myClass).CopyTo //valid with explicit.
Run Code Online (Sandbox Code Playgroud)
我主要使用explicit来保持实现干净,或者当我需要两个实现时.但不管我很少使用它.
我相信有更多的理由使用它/不使用它其他人会发布.
Phi*_*ett 193
隐式定义只是将接口所需的方法/属性等直接添加到类作为公共方法.
显式定义仅在您直接使用接口而不是底层实现时强制公开成员.在大多数情况下这是优选的.
Mat*_*ley 65
除了已经提供的优秀答案之外,还有一些情况需要显式实现,以便编译器能够找出所需的内容.看看IEnumerable<T>作为一个可能会经常出现的主要例子.
这是一个例子:
public abstract class StringList : IEnumerable<string>
{
private string[] _list = new string[] {"foo", "bar", "baz"};
// ...
#region IEnumerable<string> Members
public IEnumerator<string> GetEnumerator()
{
foreach (string s in _list)
{ yield return s; }
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)
这里,IEnumerable<string>工具IEnumerable,因此我们也需要.但是,通用版和普通版都使用相同的方法签名实现函数(C#忽略了返回类型).这是完全合法和正常的.编译器如何解决使用哪个?它迫使你只有一个隐含的定义,然后它可以解决它需要的任何东西.
即.
StringList sl = new StringList();
// uses the implicit definition.
IEnumerator<string> enumerableString = sl.GetEnumerator();
// same as above, only a little more explicit.
IEnumerator<string> enumerableString2 = ((IEnumerable<string>)sl).GetEnumerator();
// returns the same as above, but via the explicit definition
IEnumerator enumerableStuff = ((IEnumerable)sl).GetEnumerator();
Run Code Online (Sandbox Code Playgroud)
PS:IEnumerable的显式定义中的一小部分间接作用,因为在函数内部,编译器知道变量的实际类型是StringList,这就是它如何解析函数调用.实现某些抽象层的一些微不足道的事实似乎已经积累了一些.NET核心接口.
Jon*_*dal 32
当我想阻止"编程实现"(设计模式的设计原则)时,我倾向于使用显式接口实现.
例如,在基于MVP的Web应用程序中:
public interface INavigator {
void Redirect(string url);
}
public sealed class StandardNavigator : INavigator {
void INavigator.Redirect(string url) {
Response.Redirect(url);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,另一个类(例如演示者)不太可能依赖于StandardNavigator实现,并且更可能依赖于INavigator接口(因为需要将实现强制转换为接口以使用Redirect方法).
我可能采用显式接口实现的另一个原因是保持类的"默认"接口更清晰.例如,如果我正在开发ASP.NET服务器控件,我可能需要两个接口:
一个简单的例子如下.这是一个列出客户的组合框控件.在此示例中,网页开发人员对填充列表不感兴趣; 相反,他们只是希望能够通过GUID选择客户或获得所选客户的GUID.演示者将在第一页加载时填充该框,并且该演示者由控件封装.
public sealed class CustomerComboBox : ComboBox, ICustomerComboBox {
private readonly CustomerComboBoxPresenter presenter;
public CustomerComboBox() {
presenter = new CustomerComboBoxPresenter(this);
}
protected override void OnLoad() {
if (!Page.IsPostBack) presenter.HandleFirstLoad();
}
// Primary interface used by web page developers
public Guid ClientId {
get { return new Guid(SelectedItem.Value); }
set { SelectedItem.Value = value.ToString(); }
}
// "Hidden" interface used by presenter
IEnumerable<CustomerDto> ICustomerComboBox.DataSource { set; }
}
Run Code Online (Sandbox Code Playgroud)
演示者填充数据源,网页开发人员永远不需要知道它的存在.
我不建议总是使用显式接口实现.这只是两个可能有用的例子.
Val*_*zub 32
要通过C#CLR引用杰弗里里希特
(EIMI意味着Ë xplicit 我覆盖整个院落中号 ethod 我 mplementation)
了解使用EIMI时存在的某些后果至关重要.由于这些后果,你应该尽量避免使用EIMI.幸运的是,通用接口可以帮助您避免使用EIMI.但有时您可能需要使用它们(例如实现两个具有相同名称和签名的接口方法).以下是EIMI的主要问题:
- 没有文档说明类型如何专门实现EIMI方法,并且没有Microsoft Visual Studio IntelliSense支持.
- 在转换为接口时,值类型实例会被装箱.
- EIMI不能由派生类型调用.
如果使用接口引用ANY虚拟链可以在任何派生类上显式替换为EIMI,并且当将此类型的对象强制转换为接口时,将忽略您的虚拟链并调用显式实现.这不过是多态的.
EIMI还可以用于隐藏非强类型接口成员从基本框架接口的实现,如IEnumerable <T>,因此您的类不直接暴露非强类型方法,但语法正确.
Lee*_*des 19
除了已经说明的其他原因之外,这是一个类正在实现两个具有相同名称和签名的属性/方法的不同接口的情况.
/// <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
}
Run Code Online (Sandbox Code Playgroud)
此代码编译并运行正常,但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"; }
}
Run Code Online (Sandbox Code Playgroud)
请注意,显式接口定义被推断为Public - 因此您无法将它们声明为公开(或其他)显式.
另请注意,您仍然可以拥有"共享"版本(如上所示),但尽管这是可能的,但这种属性的存在是值得怀疑的.也许它可以用作Title的默认实现 - 这样就不必修改现有代码就可以将Class1强制转换为IBook或IPerson.
如果未定义"共享"(隐式)标题,则Class1的使用者必须首先将Class1的实例显式转换为IBook或IPerson,否则代码将无法编译.
sco*_*obi 15
我大部分时间都使用显式接口实现.以下是主要原因.
重构更安全
更改接口时,编译器可以检查它是否更好.隐式实现更难.
我想到两个常见的情况:
向接口添加一个函数,其中实现此接口的现有类恰好具有与新接口具有相同签名的方法.这可能导致意外行为,并且几次困扰我.调试时很难"看到",因为该函数很可能与文件中的其他接口方法无关(下面提到的自我记录问题).
从界面中删除功能.隐式实现的方法将突然死代码,但显式实现的方法将被编译错误捕获.即使死代码保持良好,我也希望被强制审查并推广它.
不幸的是,C#没有强制我们将方法标记为隐式实现的关键字,因此编译器可以进行额外的检查.由于需要使用'override'和'new',虚方法没有上述任何一个问题.
注意:对于固定或很少更改的接口(通常来自供应商API),这不是问题.但是,对于我自己的界面,我无法预测它们何时/如何改变.
它是自我记录的
如果我在一个类中看到'public bool Execute()',它将需要额外的工作来弄清楚它是接口的一部分.有人可能不得不评论它这样说,或者把它放在一组其他接口实现中,所有这些都在一个区域或分组评论中说"ITask的实现".当然,这仅在组头不在屏幕外时才有效.
鉴于:'bool ITask.Execute()'清晰明确.
明确分离接口实现
我认为界面比公共方法更"公开",因为它们被设计为只暴露具体类型的一些表面区域.它们将类型简化为一种能力,一种行为,一组特征等.在实施中,我认为保持这种分离是有用的.
当我查看类的代码时,当我遇到显式接口实现时,我的大脑转变为"代码契约"模式.这些实现通常只是转发到其他方法,但有时它们会进行额外的状态/参数检查,传入参数的转换以更好地匹配内部需求,甚至转换用于版本控制目的(即多代接口都归结为常见的实现).
(我意识到公共也是代码契约,但接口更强大,特别是在接口驱动的代码库中,直接使用具体类型通常是内部代码的标志.)
相关:Jon上面的原因2.
等等
加上其他答案中已经提到的优点:
这不是一切乐趣和快乐.在某些情况下,我坚持使用implicits:
另外,当你实际上具有具体类型并且想要调用显式接口方法时,执行转换可能会很痛苦.我用以下两种方式之一处理这个问题:
public IMyInterface I { get { return this; } }(应该内联)并调用foo.I.InterfaceMethod().如果需要此功能的多个接口,请将名称扩展到I之外(根据我的经验,我很少有这种需求).隐式接口实现是指具有相同签名的接口的方法.
显式接口实现是您显式声明方法所属的接口的位置.
interface I1
{
void implicitExample();
}
interface I2
{
void explicitExample();
}
class C : I1, I2
{
void implicitExample()
{
Console.WriteLine("I1.implicitExample()");
}
void I2.explicitExample()
{
Console.WriteLine("I2.explicitExample()");
}
}
Run Code Online (Sandbox Code Playgroud)
MSDN:隐式和显式接口实现
实现接口的每个类成员都会导出一个声明,该声明在语义上类似于VB.NET接口声明的编写方式,例如
Public Overridable Function Foo() As Integer Implements IFoo.Foo
Run Code Online (Sandbox Code Playgroud)
虽然类成员的名称通常与接口成员的名称相匹配,并且类成员通常是公共的,但这些都不是必需的.也可以声明:
Protected Overridable Function IFoo_Foo() As Integer Implements IFoo.Foo
Run Code Online (Sandbox Code Playgroud)
在这种情况下,允许类及其派生类使用名称访问类成员IFoo_Foo,但外部世界只能通过强制转换来访问该特定成员IFoo.在接口方法将在所有实现上具有指定行为的情况下,这种方法通常是好的,但仅在某些方面有用的行为[例如,只读集合的IList<T>.Add方法的指定行为是抛出NotSupportedException].不幸的是,在C#中实现接口的唯一正确方法是:
int IFoo.Foo() { return IFoo_Foo(); }
protected virtual int IFoo_Foo() { ... real code goes here ... }
Run Code Online (Sandbox Code Playgroud)
不太好.
| 归档时间: |
|
| 查看次数: |
144004 次 |
| 最近记录: |