饭后温柔

汉堡与老干妈同嚼 有可乐味
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

备忘:c#接口与抽象类

Posted on 2013-02-23 00:10  饭后温柔  阅读(2062)  评论(3编辑  收藏  举报

今年打算学习unity3d.该引擎支持c#,js,boo(似乎是python相关的一个东东),除了python算是熟悉,其他都是陌生.一番搜索,大量插件是基于c#的,较大的项目也都是基于c#.敲定.

迅速找了本<c#入门经典>,还不错,详细,就是讲的比较拖节奏.

过程式编程范型语法方面跟c++几乎一样,除了没有指针.

嗯,看到main入口函数也是放在类中,小震精了下.果然程序员都有强迫症呢,够极端.不过你也不得不是static的啊.

因为后来者,且比c++专注,多了一些方便的语法.类似out,ref,params等关键字.但行为和c++类似概念也不差多少.

看到类的时候,在c#的接口及抽象类卡了下.

初看c#的类与c++区别不大.protect,public,重载,虚函数(接口方法或抽象类方法),成员变量(属性).构造函数,析构函数,static.

区别也有,类似c#不可多继承,有静态构造函数.base,this关键字等.

刚开始看接口,还以为就是c++中的抽象接口类.接口中只有方法和属性.

(小记下属性:其实就是"C#属性是对类中的字段(fields)的保护,像访问字段一样来访问属性。同时也就封装了类的内部数据".所以就是c#帮我们定义的类似getXXX(),setXXX()隐含成员函数,外部类可以直接通过域名符号[.]来获取操作类内字段.目前未感觉到其魅力.感觉就像是"以面向对象的名义,我要封装你."这么个强买强卖的意思.)

接口不能包含实现,不可实例化.其方法都要被继承类实现.这个和c++的抽象类一个意思.但是强对象语义的c++抽象类不会在类中定义成员变量.如果不那么强迫症,弱对象语义的c++抽象类可以定义成员变量.c#接口中的属性其实并没有真正在接口中定义字段(成员变量),而只是定义其操作方法(get,set).所以目前为止c#接口与c++强的抽象类一个意思.

 

正当我志得意满的时候,作者又介绍了c#抽象类.坑爹了.幸好之前有看过com本质论等书籍,一些概念这时对理解起了作用.这次重新唤起了记忆,加深了理解.

相同处:

抽象类不能实例化.抽象类有抽象方法,自身不能实现,必须由继承类实现.

重要不同处:

抽象类只可以被单继承,接口可以被多继承.

抽象类可以包含字段.个人觉得这个是比较其与接口不同之处的很重要的着眼点.说明抽象类在类里,在内存位置上包含了一些具体的内容,而不是"虚"的了.

面向对象对类的比喻:类的成员变量,就是要模拟的物体的特征,而类的成员函数,就是要模拟的物体的行为.

于是对于2个实体,我们从抽象层面上进行对比,对它们的某一组特征和某一组行为,有4种组合:

1 特征相同/行为相同,

2 特征不同/行为不同,

3 特征相同/行为不同,

4 特征不同/行为相同.

很明显第1和第2点不需要我们探究了.

对于第3点,想象现实中的例子,比如中国人和美国人.这2个人种都有相似的生物学构造,因此行为也多有相似之处.但也有一些(一组)行为不一.如同样说话,我们说中文,他们说英语.

对于第4点,我们拿人和机器人比较.2者构造肯定不一.但都能跑,能跳,能说.

 

抽象类和接口,对于一个程序员来讲,都可以作为表达第3和第4个组合的工具.只是适合不适合的问题. 

假设我们就是那全知全能的程序员(不要不承认,我们都以为自己是上帝).

对第3点组合,我们可以创建一个人的模子(抽象类),把泥土(相同的特征材质)压进去,然后取出来,为了世界多姿多彩,每个模子的东西取出来后,我们要吹几口气(行为接口),一口气代表一种行为.接着我们做另外的模子,比如老虎,重复上边的工序.(整个时长大概要6天吧.)我们吹气的过程比较累人.这就是抽象类创建世界的过程.

我们可以换另外一种方式,因为我们是全知全能的,所以我们可以一次吹出许多不同的气(但不能换气),在此期间,我们创造所有需要这些气的东西.比如我们吹幽默,强壮2口气,在这口气结束前,我们创造所有符合这2者的人包括动物等其他东西.在这里,我们的手比较累人,因为没有模子,每次我们都要重新填材料,捏造型.这是接口的创建世界过程.

对于第3点组合来说,使用抽象类的方法比较直观,合逻辑.类似的,对于第4点组合,则使用接口的方法比较直观,合逻辑(因为特征不同,本来就没有模子).

接口表达的是"can-do"关系,抽象类表达的是"is-a"关系.接口是对世界表象所展现行为的描述.抽象类是对世界实体内在特征联系的建模.

比如假设另外一个宇宙的程序员想参观你的创造.于是你提供了一组接口给他操作.他通过接口,看到我们能跑,能飞,能耍嘴皮子.他可以通过接口说,"让子弹飞一会."于是整个宇宙能飞的都飞起来了.他并不知道飞起来的东西到底是碳基生物,还是硅基生物(他甚至不一定关心构造,上帝们通常只关心物理定律的表现).但他不可以通过接口说,"要有光.".因为这句话包含对这个宇宙属性的要求,而这是这个宇宙的全知全能者(你)才能干的事.

 

结合一点c++和com接口的经验类比,接口与抽象类,都想表达和描述一种实体间多态的关系.

对于c#接口,类似于微软的com接口概念,关注的是异质实体之间的关系.com接口甚至可以达到二进制层面的多态.

比如一旦实现相同的接口,理想世界里不管你是用c++,或者java,不管你是windows下,或linux下,我们都可以通过这个接口进行联系.接口是对表达异质实体多态的一种诉求.

当限制在c#语言层面,接口是某组行为的统一表述.接口甚至希望自己是无关语言的.

而抽象类或者类的概念,是程序里具体的代码组织关系的一种体现.关注的是同质实体之间的关系.我们提到抽象类,实际上既是被限制在某种语言内(如c#),某段代码与某段代码的某种耦合关系(子类与父类).

我们提到不同语言中2个类(或者抽象类)是没有意义的,因为它们相互之间根本互不理解.编程概念不同,底层的机器码不同,代码间的组织关系不同.

c#中的狗跟c++中的狗是风马牛不相及的2个东西.你能指望这个宇宙的程序员会跟另一个宇宙的程序员有染吗,他们连孩子都生不了.

抽象类或者说类的概念,是在某种语言环境下,对代码间的复用和对代码间的约束这2个耦合关系的一种实现手段.(希望能着重理解这句话,如果你的代码或者某种语言的代码能够很好的处理这2个耦合关系,那是否使用面向对象,是否有类体系根本就是无关紧要的.)

 

因此,接口实际上是比类更基础,更广泛的一个概念,因此类(包括抽象类)可以继承接口.早期语言如c++因为诞生年代较早,语言中没有内建这一概念,现在软件基础世界的一些弊端,也由此而来,比如dll地狱.c#作为后来者,在语言中实现了对这个概念的追求,虽然还是被限制在了语言层面上(但理想的接口基本是不可能的,它需要各种通用标准愿意一起支持).

 

看到这里,读者应该明白接口是独立于类体系之外的概念.但有位读者指出我讨论接口和抽象类,就说明我根本没明白接口.显然他只看了标题,我想可能还需要继续补充下.

接口可以被多"继承".我们已经明白接口不是针对实体的内在特征的建模,只是对一组行为的表述和期待.

显然类可以有接口,但接口和类不是一个范畴,为什么类要继承接口呢?其实不过是一种表述或语法上的方便罢了.也许我们应该把继承换成另外的词,如"约束"等等,表明类有该接口.

既然c#已经实现了类体系编程范式,而接口方法与抽象类成员方法有一些联系(比如都是函数,都必须被特定实现),接口在语言编译实现方面跟抽象类肯定是有一些类似的,就c#的作者而言,我觉得他们让接口是被"继承"的是一种自然而然的想法.不必又添加类似关键字等等各种细节.只要我们明白继承接口和继承抽象类概念上不是一个范畴即可.

 

关于com组件更深的介绍可以参考<com本质论>等相关书籍.个人建议读前面几章了解来龙去脉,获得核心观点其实已经足够.后边的太累赘.