接口的相关陈述
接口的理解
面向接口编程利用 OO 的一个基本性质——多态,相同方法不同表现。可以这样想一下,client 编写自己程序的时候,如果直接面向一个具体类写程序,那这个程序有个风吹草动的,那 client 就要受到影响,但如果面向一个接口就不同了,某个具体类变了,只知接口,不知具体类的 client 就可以完全不动。 都说上层领导比较好当,因为可以干的事通常对老百姓来说是虚的,越虚就越不容易错。
这个道理在 OO 中也是适用的。
换个视角看,面向接口编程反映 OO 的另一个方面——封装,接口将具体实现封装了起来,可以不影响客户的情况下切换实现
接口的作用,一言以蔽之,就是标志类的类别(type of class)。把不同类型的类归于不同的接口,可以更好的管理他们。OO 的精髓,我以为,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如 c++、java、c# 等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。(cowboy 的名言是“抽象就是抽去像的部分”,看似调侃,实乃至理)。
空接口的使用
在接口使用的时候,空接口有 2 种情况:
1.类似于 ObjectBuilder 中的 IBuilderPolicy,他们往往是做一个标记,表示需要某个功能。当然你也可以这么用,来表示你的类具有某个功能,实现了你的某个接口。
2.你的接口继承了别的接口(非空),你的接口本身没有声明函数。这种情况一般是你不希望用户使用父接口作为参数类型,因为他们的用途可能不同,此时就可以用空接口来实现。
可以看到,Text 接口是用于返回一个字符串。而 SqlText 是一个空接口,它继承了 Text 接口。也就是说 SqlText 也是一种 Text。但是我们可以知道,任何一个字符串不一定是 Sql 字符串,所以此时声明了一个 SqlText 接口来用于表名当前的字符串是一个 Sql 字符串。你的函数可以这样声明:
而不是这样:
避免用户产生歧义的想法,一眼看去,就明白应该传入一个 Sql 字符串。
接口的成员为什么没有委托
我们都知道 C# 的接口是可以包含事件的,其实当我们看到事件的时候,很容易就会想到委托,委托是事件的基础,如果对委托和事件不是特别清楚的程序员就一定不会明白,为什么 C# 接口中可以包含事件而不能有委托呢。其实简单的说法就是委托也是类型,delegate 关键字引入的是一个新的类型,所以一个 C# 接口无法包容一个委托并把它当作成员;而 event 关键字引入的是一个新的成员,因此事件可以归人接口。理解这点,我们要从 C# 接口的使命说起,C# 接口是一个契约,规范了接口实现者的行为,而不是要有些什么。很简单,例如“党员”是个接口,它肯定有个动作是“为人民服务”,“某某党员”实现了“党员”这个接口,那么“某某党员”肯定也要“为人民服务”,至于你“某某党员”是否必须拥用“电脑”、“小孩”。那么“党员”这个接口中肯定不会有规定。这也就是接口的目的,规范了实现者的一些行为。所以 C# 接口的成员都是方法,不会有其它了。稍有 C# 常识的程序员都明白,C# 中的属性,其实就是两个方法,一个 Set 方法,一个 Get 方法,同样事件和索引器也都是方法,请看下面的接口:
该接口包含了 C# 接口所能接纳的所有成员,事件,属性,索引器,方法。把该接口编译后,我们用 MSIL Disassembler 工具查看一下:
这下大家都明白了,其实属性 Name 对应于 Get_Name()、Set_Name() 这两个方法,事件 OnDraw 对应于 add_OnDraw()、remove_OnDraw() 这两个方法,索引器对应于 get_Item()、set_Item() 这两个方法。在看下面的委托和类的定义:
看到了吧,定义一个委托和定义一个类是没有什么区别的,都是定义了个新的类型。所以 C# 接口是不能有委托的,除非微软告诉我们 C# 接口中是可以定义类的。