接口是把公共实例(非静态)方法和属性组合起来,以封装特定功能的一个集合。一旦定义了接口,就可以在类中实现它。这样,类就可以支持接口所指定的所有属性和成员。
注意,接口不能单独存在。不能像实例化一个类那样实例化接口。另外,接口不能包含实现其成员的任何代码,而只能定义成员本身。实现过程必须在实现接口的类中完成。
在前面的咖啡示例中,可以把通用属性和方法,例如 AddSugar()
、Milk
、Sugar
和 Instant
组合到一个接口中,这个接口可以命名为 IHotDrink
(接口的名称一般用大写字幕 I
开头)。然后就可以在其他对象上使用该接口,例如 CupOfTea
类的对象。所以可以采用类似的方式处理这些对象,而对象仍保有自己的属性(例如 CupOfCoffee
仍有属性 BeanType
,CupOfTea
仍有属性 LeafType
)。
在 UML 中,在对象上实现的接口用 “棒棒糖” 语法来表示。在图 8-6 中,用与类相似的语法把 IHotDrink
的成员放在一个单独的框中。
一个类可以支持多个接口,多个类也可以支持相同的接口。所以接口的概念让用户和其他开发人员更容易理解其他人的代码。例如,有一些代码使用一个带某接口的对象。假定不使用这个对象的其他属性和方法,就可以用另一个对象代替这个对象(例如,使用上述 IHotDrink
接口的代码可以处理 CupOfCoffee
和 CupOfTea
实例)。另外,该对象的开发人员可以提供该对象的更新版本,只要它支持已经在用的接口,就可以在代码中使用这个新版本。
在发布接口后,即接口可以用于其他开发人员或终端用户后,最好不要修改它。理解这一点的一种方式是把接口看成类的创建者和使用者之间的契约,即 “每个支持接口 X
的类都支持这些方法和属性”。如果以后修改了接口,也许是升级了底层的代码,该接口的使用者就不能正确运行接口,甚至失败。所以,我们应做的是创建一个新的接口,使其扩展旧接口,可能还包含一个版本号,如 X2
。这是创建接口的标准方式,以后我们会常常遇到编了号的接口。
可删除的对象
IDisposable
接口特别有趣。支持 IDisposable
接口的对象必须实现其 Dispose()
方法,即它们必须提供这个方法的代码。当不再需要某个对象(例如,在对象超出作用域之前)时,就调用这个方法,释放重要资源,否则,等到对来记回收调用析构方法时才会释放资源。这样可以更好地控制对象所使用的资源。
C#允许使用一种可以优化使用这个方法的结构。 using
关键字可以在代码块中初始化使用重要资源的对象,在这个代码块的末尾会自动调用 Dispose()
方法,用法如下:
<ClassName> <VariableName> = new <ClassName>(); ... using (<VariableName>) { ... }
或者把初始化对象 <VaribaleName>
作为 using
语句的一部分:
using(<ClassNmae><VariableName> = new<ClassName>()) { ... }
这两种情况下,可以在 using
代码块中使用变量 <VariableName>
,并在代码块的末尾自动删除(在代码块执行完毕后,调用 Dispose()
)。