Notifier's Blog

常遇困境,说明你在进步!
       常有压力,说明你有目标!
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

C#.NET学习笔记9---C#中的接口

Posted on 2010-05-17 10:55  notifier  阅读(2493)  评论(4)    收藏  举报

  C#中的接口与Java类似,略有不同。

1.  接口的定义

  C#中接口可以包含一个或多个成员,这些成员可以是方法、属性、索引指示器和事件,但不能是常量、域操作符、构造函数或析构函数,而且不能包含任何静态成员。

  接口成员默认访问方式是public,接口成员声明不能包含任何修饰符,比如成员声明前不能加abstract, public, protected, internal, private, virtual, override 或static 修饰符。

  给一个接口的定义实例:

interface IExample
{
//注意: 与Java不同的是,C#中接口声明不能添加任何修饰符,默认是public

//定义方法
void F(int value);
//定义属性
string P { get; set; }
//定义索引指示器
string this[int index] { get; set; }
//定义事件
event EventHandler E;
}

 2.  对接口成员的访问

  由于接口是支持多继承的,在多继承中,如果两个父接口都包含了同名的成员,这就会产生二义性,这时需要进行显式的声明。

  如下面的实例:

using System;
interface IInteger
{
void Add(int i);
}
interface IDouble
{
void Add(double d);
}
interface INumber : IInteger, IDouble { }
class C
{
void Test(INumber n)
{
//n.Add(1); 错误
((IInteger)n).Add(1); // 正确
((IDouble)n).Add(1); // 正确
}
}

/*
* 调用n.Add(1) 会导致二义性,因为候选的重载方法的参数类型均适用.
* 只要加入了显式的指派就决不会产生二义性
*/

   接口的多继承也带来了一定的问题,这里主要是使用new覆盖的问题,例如下面的示例:

interface IBase
{
void F(int i);
}
interface ILeft : IBase
{
new void F(int i);
}
interface IRight : IBase
{
void G();
}
interface IDerived : ILeft, IRight { }
class A
{
void Test(IDerived d)
{
d.F(
1); // 调用 ILeft.F
((IBase)d).F(1); // 调用 IBase.F
((ILeft)d).F(1); // 调用 ILeft.F
((IRight)d).F(1); // 调用 IBase.F
}
}

/*
* 方法IBase.F 在派生的接口ILeft 中被 Ileft 的成员方法F 覆盖了,所以对 d.F(1) 的调用实际上调用了ILeft.F.
* 虽然从IBase IRight IDerived 这条继承路径上来看, ILeft.F 方法是没有被覆盖的
* 我们只要记住这一点: 一旦成员被覆盖以后,所有对其的访问都被覆盖以后的成员拦截了
*/

3.  接口的实现

  接口的实现是在类或者结构中,主要是在类中。 这部分与Java类似,不同的部分见下面示例描述:

   3.1 显式接口成员

using System;
interface IControl
{
void Paint();
}
interface IDataBound
{
void Bind(Binder b);
}
public class Control: IControl
{
public void Paint()
{
//do somethings
}
}
public class EditBox: Control, IControl, IDataBound
{
public void Paint()
{
// do somethings
}
public void Bind(Binder b)
{
//do somethings
}
}

/*
* 类EditBox 从Control 类继承并同时实现了IControl 和 IDataBound 接口
* EditBox 中的Paint 方法来自IControl 接口,Bind 方法来自 IDataBound 接口
* 二者在 EditBox 类中都作为公有成员实现, 当然在C# 中我们也可以选择不作为公有成员实现接口
* 如果每个成员都明显地指出了被实现的接口,通过这种途径被实现的接口,我们称之为显式接口成员explicit interface member
*/

   显式接口成员的特点是:只能被接口调用,例如如下示例:

public class EditBox: IControl, IDataBound
{
void IControl.Paint()
{
//do somethings
}
void IDataBound.Bind(Binder b)
{
//do somethings
}
}

class Test
{
static void Main()
{
EditBox editbox
= new EditBox();
editbox.Paint();
// error: no such method
IControl control = editbox;
control.Paint();
// calls EditBox’s Paint implementation
}
}

/*
* 上述代码中对editbox.Paint()的调用是错误的,因为editbox 本身并没有提供这一方法.
* control.Paint()才是正确的调用方式
*/

    3.2 接口的映射

  类必须为在声明中列出的所有接口成员提供具体的实现。在类中定位接口成员的实现称之为接口映射(interface mapping)。

  相关示例如下:

interface IControl
{
void Paint();
}
class Control: IControl
{
public void Paint()
{
//do somethings
}
}
class TextBox: Control
{
new public void Paint()
{
//do somethings
}
}

/*
* TextBox 中的Paint 方法覆盖了Control 中的Paint 方法,但却没有改变Control.Paint 对 IControl.Paint 的映射
* 在类的实例和接口的实例中对Paint方法的调用会产生下面这样的结果:
* Control c = new Control();
* TextBox t = new TextBox();
* IControl ic = c;
* IControl it = t;
* c.Paint(); // invokes Control.Paint();
* t.Paint(); // invokes TextBox.Paint();
* ic.Paint(); // invokes Control.Paint();
* it.Paint(); // invokes Control.Paint();
* 如果需要让it去调用TextBox.Paint(),就要用virtual方法去实现Control中的Paint,然后再TextBox中new一个Paint()
*/

   3.3 接口的重实现(也就是重映射)

  所谓接口的重实现,其实就是接口的重映射。示例如下:

interface IControl
{
void Paint();
}
class Control: IControl
{
void IControl.Paint()
{
//do somethings
}
}
class MyControl: Control, IControl
{
public void Paint() {}
}

/*
* Control 把IControl.Paint 映射到了Control.IControl.Paint 上,
* 但这并不影响在MyControl 中的重实现(重映射)
* 在MyControl 中的重实现中,IControl.Paint 被映射到MyControl.Paint 之上
*/

    3.4 抽象类与接口

  其实接口就是一种纯的抽象类,与接口不同的是:抽象类可以继承接口,把接口映射到抽象函数。示例如下:

interface IMethods
{
void F();
void G();
}
abstract class C : IMethods
{
//这里将IMethods的F()和G()映射到了类C的抽象方法上
public abstract void F();
public abstract void G();
}