接口的实现

  在继续前,先讨论一下如何定义和实现接口。第 9 章介绍过接口的定义方式与类相似,使用的代码如下:

  1. interface IMyInterface
  2. {
  3. // Interface members.
  4. }

  接口成员的定义与类成员的定义相似,但具有几个重要的区别:

  1. 不允许使用访问修饰符(publicprivateprotected internal),所有接口成员都隐式地是公共的。
  2. 接口成员不能包含代码体。
  3. 接口不能定义字段成员。
  4. 不能用关键字 staticvirtualabstract sealed 来定义接口成员。
  5. 类型定义成员是禁止的。

  但要隐藏从基接口中继承的成员,可以用关键字 new 来定义它们,例如:

  1. interface IMyBaseInterface
  2. {
  3. void DoSomething();
  4. }
  5. interface IMyDerivedInterface : IMyBaseInterface
  6. {
  7. new void DoSomething();
  8. }

  其方式与隐藏继承的类成员的方法一样。

  在接口中定义的属性可以定义访问块 getset 中的哪一个能用于该属性(或将它们同时用于该属性),例如:

  1. interface IMyInterface
  2. {
  3. int MyInt { get; set; }
  4. }

  其中 int 属性 MyIntgetset 存取器。对于访问级别有严格限制的属性来说,可以省略它们的任一个。

  这个语法类似于自动属性,但自动属性是为类(而不是接口)定义的,自动属性必须包含 getset 存取器。

  接口没有指定应如何存储属性数据。接口不能指定字段,例如用于存储数据的字段。最后,接口与类一样,可以定义为类的成员(但不能定义为其他接口的成员,因为接口不能包含类型定义)。

在类中实现接口

  实现接口的类必须包含该接口所有成员的实现代码,且必须匹配指定的签名(包括匹配指定的 getset 块),并且必须是公共的。例如:

  1. public interface IMyInterface
  2. {
  3. void DoSomething();
  4. void DoSomethingElse();
  5. }
  6. public class MyClass : IMyInterface
  7. {
  8. public void DoSomething()
  9. {
  10. }
  11. public void DoSomethingElse()
  12. {
  13. }
  14. }

  可使用关键字 virtualabstract 来实现接口成员,但不能使用 staticconst。还可以在基类上实现接口成员,例如:

  1. public interfce IMyInterface
  2. {
  3. void DoSomething();
  4. void DoSomethingElse();
  5. }
  6. public class MyBaseClass
  7. {
  8. public void DoSomething();
  9. }
  10. public class MyDerivedClass : MyBaseClass, IMyInterface
  11. {
  12. public void DoSomethingElse()
  13. {
  14. }
  15. }

  继承一个实现给定接口的基类,就意味着派生类隐式地支持这个接口,例如:

  1. public interface IMyInterface
  2. {
  3. void DoSomething();
  4. void DoSomethingElse();
  5. }
  6. public class MyBaseClass : IMyInterface
  7. {
  8. public virtual void DoSomething()
  9. {
  10. }
  11. public virtual void DoSomethingElse()
  12. {
  13. }
  14. }
  15. public class MyDerivedClass : MyBaseClass
  16. {
  17. public override void DoSomething()
  18. {
  19. }
  20. }

  显然,在基类中把实现代码定义为虚拟,派生类就可以替换该实现代码,而不是隐藏它们。如果要使用 new 关键字隐藏一个基类成员,而不是重写它,则方法 IMyInterface.DoSomething() 就总是引用基类版本,即使通过这个接口来访问派生类,也是这样。

  1. 显式实现接口成员

  也可以由类显式地实现接口成员。如果这么做,就只能通过接口来访问该成员,不能通过类来访问。上一节的代码中使用的隐式成员可以通过类和接口来访问。

  例如,如果类 MyClass 隐式地实现接口 IMyInterface 的方法 DoSomething(),如上所述,则下面的代码就是有效的:

  1. MyClass myObj = new MyClass();
  2. myObj.DoSomething();

  下面的代码也是有效的:

  1. MyClass myObj = new MyClass();
  2. IMyInterface myInt = myObj;
  3. myInt.DoSomething();

  另外,如果 MyDerviedClass 显式地实现 DoSomething(),就只能使用后一种技术。其代码如下:

  1. public class Myclass : IMyInterface
  2. {
  3. void IMyInterface.DoSomething()
  4. {
  5. }
  6. public void DoSomethingElse()
  7. {
  8. }
  9. }

  其中 DoSomething() 是显式实现的,而 DoSomethingElse() 是隐式实现的。只有后者可以直接通过 MyClass 的对象实例来访问。

  2. 其他属性存取器

  前面说过,如果实现带属性的接口,就必须实现匹配的 get/set 存取器。这并不是绝对正确的—如果在定义属性的接口中只包含 set 块,就可给类中的属性添加 get 块,反之亦然。但只有隐式实现接口时才这么做。另外,大多数时候,都想让所添加的存取器的可访问修饰符比接口中定义的存取器的可访问修饰符更严格。因为按照定义,接口定义的存取器是公共的,也就是说,只能添加非公共的存取器。例如:

  1. public interface IMyInterface
  2. {
  3. int MyIntProperty
  4. {
  5. get;
  6. }
  7. }
  8. public class MyBaseClass : IMyInterface
  9. {
  10. public int MyIntProperty { get; protected set; }
  11. }

  如果将新添加的存取器定义为公共的,那么能够访问实现该接口的类的代码页可以访问该存取器。但是,只能访问接口的代码就不能访问该存取器。