分享

接口的显式实现和隐式实现

 穆巴 2013-04-11

接口可以多继承,有些时候我们可能需要解决命名空间冲突。

using System;
using System.Linq;
using System.Text;
namespace Test
{
public interface IDraw1
{
void Draw();
}
public interface IDraw2
{
void Draw();
}
class MyDraw : IDraw1, IDraw2
{
public void Draw()
{
Console.WriteLine("Drawing...");
}
}
class Program
{
static void Main(string[] args)
{
MyDraw md = new MyDraw();
md.Draw();
((IDraw1)md).Draw();
((IDraw2)md).Draw();
Console.ReadKey();
}
}
}

 以上的方式是隐式的实现接口,这里就出现了一个问题:只提供一个Draw()方法的实现,导致了两个接口都使用该成员作为它们的实现。其运行结果如下:


显然这不是我们想要的,可以使用显式接口实现语法解决命名空间冲突,其代码如下:

using System;
using System.Linq;
using System.Text;
namespace Test
{
public interface IDraw1
{
void Draw();
}
public interface IDraw2
{
void Draw();
}
class MyDraw : IDraw1, IDraw2
{
void IDraw1.Draw()
{
Console.WriteLine("Drawing1...");
}
void IDraw2.Draw()
{
Console.WriteLine("Drawing2...");
}
}
class Program
{
static void Main(string[] args)
{
MyDraw md = new MyDraw();
    ((IDraw1)md).Draw();
((IDraw2)md).Draw();
    Console.ReadKey();
}
}
}

显式实现成员是隐式私有的,这些成员在对象级别就不可用,我们必须显示转换来访问需要的功能。(使用if(obj is IObjType)先判断下类型再进行转换是个不错的选择)

其运行结果如下:

可见:

1 隐式实现接口,接口和类(实现接口的类)都可以访问类中的方法;

2 显式实现接口,C#没有提供任何语法,来在派生类中调用基类中显式实现的接口成员,只有通过接口来访问类中的方法;

3 显式实现接口,可以帮助我们在对象级别隐藏高级成员。但是,不要把显式实现当做安全壁垒。只要把实例强制转换为接口,任何代码都可以调用此类的方法。

何时使用隐式接口实现 如果没有很强的理由。就使用隐式接口实现。显式的接口实现可能会把开发人员搞糊涂,因为这些成员是隐式私有的,不会出现在公有成员的列表中,而且对值类型来说还会导致不必要的装箱。把值类型转换为接口是调用显式实现的接口成员的唯一方法,这会导致装箱,这可能违背我们使用值类型的初衷——低开销。

何时使用显式接口实现

1 接口多继承,引起命名空间冲突。

2 当需要在开发过程中定义一些只供内部使用的接口时。

3 类型已经有了一个方法,该方法与接口方法的名字和参数相同,但返回类型不同。

4 如果希望接口成员只能通过该接口来调用。例如ICollection<T>.IsReadonly的主要目的是为了让数据绑定基础设施通过ICollection<T>接口来访问。在实现该接口类型时,几乎不会直接访问该方法。因此,IList<T>显式地实现了该接口成员。

5 模拟变体(variance)。(在被覆盖的成员中改变参数或返回值的类型。)例如,为了创建强类型集合,IList的实现通常会显式地实现(隐藏)弱类型成员,并增加强类型的公有成员,以改变参数和返回值的类型。

public class StringCollection:IList{
public string this[int index]{...}
object IList.this[int index]{...}
...
}

6 在需要隐藏一个成员并增加另一个名字更合适的等价成员时。这等同对成员进行重命名。例如,System.IO.FileStraem显式地实现了IDispossable.Dispose,并将它重命名为Close。

public class FileStream:IDisposable
{
void IDisposable.Dispose()
{
Close();
}
public void Close(){...}
...
}

这样的用法并不推荐,重命名一个好的名字与沿用一个糟糕的名字相比,可能会引起更大的混乱。例如:Close()是应该调用IDispossable.Dispose()还是某个其它方法?开发人员是否知道这两个函数实际上是同样的东西?是否有潜在的调用顺序?这些问题可能会困扰开发人员。

注意,如果希望让对显式实现的接口成员功能进行定制,那么要为其提供具有相同功能的受保护的虚成员。显式实现的成员不能被覆盖。虽然可以重新定义它们,但之后的子类型就不可能再调用基类方法的实现了。


[Serializable]
public class List<T>:ISerializable
{
void ISerializable.GetObjectData(SerializationInfo info,StreamingContext context)
{
GetObjectData(info,context);
}
protected virtual void GetObjectData(SerializationInfo info,StreamingContext context)
{
...
}
....
}

该用法请慎用,因为子类可能会包含恶意代码。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多