发布日期:2007.4.12 作者:Anytao
©2007 Anytao.com 转贴请注明出处,留此信息。
本文将介绍以下内容:
• 面向对象思想:多态
• 接口
• 抽象类
1. 引言
在我之前的一篇post《抽象类和接口的谁是谁非》中,和同事管伟的讨论,得到很多朋友的关注,因为是不成体系的论道,所以给大家了解造成不便,同时关于这个主题的系统性理论,我认为也有必要做以总结,因此才有了本篇的新鲜出炉。同时,我将把上贴中的问题顺便也在此做以交代。
2. 概念引入
接口是包含一组虚方法的抽象类型,其中每一种方法都有其名称、参数和返回值。接口方法不能包含任何实现,CLR允许接口可以包含事件、属性、索引器、静态方法、静态字段、静态构造函数以及常数。但是注意:C#中不能包含任何静态成员。一个类可以实现多个接口,当一个类继承某个接口时,它不仅要实现该接口定义的所有方法,还要实现该接口从其他接口中继承的所有方法。
定义方法为:
public interface System.IComparable
{
int CompareTo(object o);
}
public class TestCls: IComparable
{
public TestCls()
{
}
private int _value;
public int Value
{
get { return _value; }
set { _value = value; }
}
public int CompareTo(object o)
{
//使用as模式进行转型判断
TestCls aCls = o as TestCls;
if (aCls != null)
{
//实现抽象方法
return _value.CompareTo(aCls._value);
}
}
}
抽象类提供多个派生类共享基类的公共定义,它既可以提供抽象方法,也可以提供非抽象方法。抽象类不能实例化,必须通过继承由派生类实现其抽象方法,因此对抽象类不能使用new关键字,也不能被密封。如果派生类没有实现所有的抽象方法,则该派生类也必须声明为抽象类。另外,实现抽象方法由overriding方法来实现。
定义方法为:
/// <summary>
/// 定义抽象类
/// </summary>
abstract public class Animal
{
//定义静态字段
protected int _id;
//定义属性
public abstract int Id
{
get;
set;
}
//定义方法
public abstract void Eat();
//定义索引器
public string this[int i]
{
get;
set;
}
}
/// <summary>
/// 实现抽象类
/// </summary>
public class Dog: Animal
{
public override int Id
{
get {return _id;}
set {_id = value;}
}
public override void Eat()
{
Console.Write("Dog Eats.")
}
}
3. 相同点和不同点
3.1 相同点
- 都不能被直接实例化,都可以通过继承实现其抽象方法。
- 都是面向抽象编程的技术基础,实现了诸多的设计模式。
3.2 不同点
- 接口支持多继承;抽象类不能实现多继承。
- 接口只能定义抽象规则;抽象类既可以定义规则,还可能提供已实现的成员。
- 接口是一组行为规范;抽象类是一个不完全的类,着重族的概念。
- 接口可以用于支持回调;抽象类不能实现回调,因为继承不支持。
- 接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法;抽象类可以定义字段、属性、包含有实现的方法。
- 接口可以作用于值类型和引用类型;抽象类只能作用于引用类型。例如,Struct就可以继承接口,而不能继承类。
通过相同与不同的比较,我们只能说接口和抽象类,各有所长,但无优略。在实际的编程实践中,我们要视具体情况来酌情量才,但是以下的经验和积累,或许能给大家一些启示,除了我的一些积累之外,很多都来源于经典,我相信经得起考验。所以在规则与场合中,我们学习这些经典,最重要的是学以致用,当然我将以一家之言博大家之笑,看官请继续。
3.3 规则与场合
- 请记住,面向对象思想的一个最重要的原则就是:面向接口编程。
- 借助接口和抽象类,23个设计模式中的很多思想被巧妙的实现了,我认为其精髓简单说来就是:面向抽象编程。
- 抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。
- 接口着重于CAN-DO关系类型,而抽象类则偏重于IS-A式的关系;
- 接口多定义对象的行为;抽象类多定义对象的属性;
- 接口定义可以使用public、protected、internal 和private修饰符,但是几乎所有的接口都定义为public,原因就不必多说了。
- “接口不变”,是应该考虑的重要因素。所以,在由接口增加扩展时,应该增加新的接口,而不能更改现有接口。
- 尽量将接口设计成功能单一的功能块,以.NET Framework为例,IDisposable、IDisposable、IComparable、IEquatable、IEnumerable等都只包含一个公共方法。
- 接口名称前面的大写字母“I”是一个约定,正如字段名以下划线开头一样,请坚持这些原则。
- 在接口中,所有的方法都默认为public。
- 如果预计会出现版本问题,可以创建“抽象类”。例如,创建了狗(Dog)、鸡(Chicken)和鸭(Duck),那么应该考虑抽象出动物(Animal)来应对以后可能出现风马牛的事情。而向接口中添加新成员则会强制要求修改所有派生类,并重新编译,所以版本式的问题最好以抽象类来实现。
- 从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实实现。
- 对抽象类不能使用new关键字,也不能被密封,原因是抽象类不能被实例化。
- 在抽象方法声明中不能使用 static 或 virtual 修饰符。
以上的规则,我就厚颜无耻的暂定为T14条吧,写的这么累,就当一时的奖赏吧。大家也可以互通有无,我将及时修订。
4. 经典示例
4.1 绝对经典
.NET Framework是学习的最好资源,有意识的研究FCL是每个.NET程序员的必修课,关于接口和抽象类在FCL中的使用,我有以下的建议:
- FCL对集合类使用了基于接口的设计,所以请关注System.Collections中关于接口的设计实现;
- FCL对数据流相关类使用了基于抽象类的设计,所以请关注System.IO.Stream类的抽象类设计机制。
4.2 别样小菜
下面的实例,因为是我的理解,因此给经典打上“相对”的记号,至于什么时候晋升为“绝对”,就看我在.NET追求的路上,是否能够一如既往的如此执着,因此我将把相对重构到绝对为止(呵呵)。 本示例没有阐述抽象类和接口在设计模式中的应用,因为那将是另一篇有讨论价值的文本,本文着眼与概念和原则的把握,但是真正的应用来自于具体的需求规范。
设计结构如图所示:
1. 定义抽象类
public abstract class Animal
{
protected string _name;
//声明抽象属性
public abstract string Name
{
get;
}
//声明抽象方法
public abstract void Show();
//实现一般方法
public void MakeVoice()
{
Console.WriteLine("All animals can make voice!");
}
}
2. 定义接口
public interface IAction
{
//定义公共方法标签
void Move();
}
3. 实现抽象类和接口
public class Duck : Animal, IAction
{
public Duck(string name)
{
_name = name;
}
//重载抽象方法
public override void Show()
{
Console.WriteLine(_name + " is showing for you.");
}
//重载抽象属性
public override string Name
{
get { return _name;}
}
//实现接口方法
public void Move()
{
Console.WriteLine("Duck also can swim.");
}
}
public class Dog : Animal, IAction
{
public Dog(string name)
{
_name = name;
}
public override void Show()
{
Console.WriteLine(_name + " is showing for you.");
}
public override string Name
{
get { return _name; }
}
public void Move()
{
Console.WriteLine(_name + " also can run.");
}
}
4. 客户端实现
public class TestAnmial
{
public static void Main(string [] args)
{
Animal duck = new Duck("Duck");
duck.MakeVoice();
duck.Show();
Animal dog = new Dog("Dog");
dog.MakeVoice();
dog.Show();
IAction dogAction = new Dog("A big dog");
dogAction.Move();
}
}
5. 他山之石
正所谓真理是大家看出来的,所以将园子里有创新性的观点潜列于此,一是感谢大家的共享,二是完善一家之言的不足,希望能够将领域形成知识,受用于我,受用于众。
- dunai认为:抽象类是提取具体类的公因式,而接口是为了将一些不相关的类“杂凑”成一个共同的群体。至于他们在各个语言中的句法,语言细节并不是我关心的重点。
- 桦山涧的收藏也很不错。
- Artech认为:所代码共用和可扩展性考虑,尽量使用Abstract Class。当然接口在其他方面的优势,我认为也不可忽视。
- shenfx认为:当在差异较大的对象间寻求功能上的共性时,使用接口;当在共性较多的对象间寻求功能上的差异时,使用抽象基类。
最后,MSDN的建议是:
- 如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单易行的方法来控制组件版本。通过更新基类,所有继承类都随更改自动更新。另一方面,接口一旦创建就不能更改。如果需要接口的新版本,必须创建一个全新的接口。
- 如果创建的功能将在大范围的全异对象间使用,则使用接口。抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。
- 如果要设计小而简练的功能块,则使用接口。如果要设计大的功能单元,则使用抽象类。
- 如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。抽象类允许部分实现类,而接口不包含任何成员的实现。
6. 结论
接口和抽象类,是论坛上、课堂间讨论最多的话题之一,之所以将这个老话题拿出来再议,是因为从我的体会来说,深刻的理解这两个面向对象的基本内容,对于盘活面向对象的抽象化编程思想至关重要。本文基本概况了接口和抽象类的概念、异同和使用规则,从学习的观点来看,我认为这些总结已经足以表达其核心。但是,对于面向对象和软件设计的深入理解,还是建立在不断实践的基础上,Scott说自己每天坚持一个小时用来写Demo,那么我们是不是更应该勤于键盘呢。对于接口和抽象类,请多用而知其然,多想而知其奥吧。
参考文献:
(USA)Jeffrey Richter, Applied Microsoft .NET Framework Programming
©2007 Anytao.com 转贴请注明出处,留此信息。
本贴子以“现状”提供且没有任何担保,同时也没有授予任何权利。
This posting is provided "AS IS" with no warranties, and confers no rights.
posted @ 2007-04-12 14:33
Anytao 阅读(35577)
评论(226) 编辑 收藏
发表评论
@Cloud87
回答你第二个问题
接口不是重写方法 而是实现方法,接口只是提出了一个方法名,具体的功能你要提供
接口主要用来为各个对象指定需要实现的方法名,这些对象可能没什么联系
你可以把接口想象成手机充电器的端口,这里就是显然没有用接口,每个手机都自己提供一个自己的端口充电,我换个手机原来的充电器就不能用了。
如果手机(对象)都实现用相同的端口(接口),那充电器(充电器对象)不需要做调整就能正常使用,甚至如果我的笔记本(新的对象)也实现了这个端口(同一个接口)这个充电器(充电器对象)也能正常使用
@Cloud87
第一个问题:
首先
private int _value;
public int Value
{
get { return _value; }
/value是set访问器的一个隐含参数,为小写,用于从客户端传值
set { _value = value; }
}
例如,如果重新定义一个属性:
private string _name;
public string Name
{
get {return _value;}
set {_name = value;} //value是一个隐含参数
}
public int CompareTo(object o)
{
//使用as模式进行转型判断
TestCls aCls = o as TestCls;
if (aCls != null)
{
//_value本身是int型,所以_value.CompareTo调用的是Int.CompareTo
//而不是TestInterface.CompareTo,不存在任何循环调用
return _value.CompareTo(aCls._value);
}
}
}
@Cloud87
SAIZONE的举例很且贴。正如你所说,Student可以不需要继承抽象类,可以不需要实行接口,一样可以实现具有Name属性和DoSomething行为,这是无可争议的。
而接口和抽象类存在的更广泛意义,在于面向抽象编程,面向对象思维。如果在你的当前需求下,如果需要新增一个教师类,那么这个类同样也会具有Name和DoSomething,此时你必须重新思考原来对Student的设计,而面向对象的继承、多态就正体现在了Abstract Class和Interface的灵活设计上了。
这是个需要不断积累的过程,最好了解一下相关的面向对象、设计原则和设计模式,你的思路就会更加开阔。
感谢你们的回答....明白了很多^^我会常来看看的^^
@风吹屁屁凉
呵呵,你好,国外应该是没有发行。刚刚出版,还是个新生作品,可以托国内朋友看看,不过邮费实在不划算呐:-)
@wizardlun
呵呵,谢谢关注,一如既往:-)
@agp001
事实上,还是有很多的不同,书上的内容更加完善和完整,也仔细修改了部分问题。另外,网上已发表内容只有全书内容的1/6内容量:-)
真的很佩服。
学习中问个问题:
-------------------------
你可以把接口想象成手机充电器的端口,这里就是显然没有用接口,每个手机都自己提供一个自己的端口充电,我换个手机原来的充电器就不能用了。
如果手机(对象)都实现用相同的端口(接口),那充电器(充电器对象)不需要做调整就能正常使用,甚至如果我的笔记本(新的对象)也实现了这个端口(同一个接口)这个充电器(充电器对象)也能正常使用
------------------------
如果有个返回电压的方法,N记手机实现接口的电压方法时返回的电压是5V,M记实现的方法返回的是3V,那他们同样实现了相同的方法,但是也不能通用吧?
谢谢!
这是个需要不断积累的过程,最好了解一下相关的面向对象、设计原则和设计模式,你的思路就会更加开阔。
@finer
您好,我不是很明白你所指的:
-------------------------
N记手机实现接口的电压方法时返回的电压是5V,M记实现的方法返回的是3V
-------------------------
这句话的意思,可否以代码实现,这样表述会更清楚,我们再针对代码进行讨论?
谢谢你的讨论:-)
如果有个返回电压的方法,N记手机实现接口的电压方法时返回的电压是5V,M记实现的方法返回的是3V,那他们同样实现了相同的方法,但是也不能通用吧?
---------------------------------------------------------------
例:
public interface IMobileTelephone
{
int GetVoltage();
}
//N记手机
public class NokiaMobileTelephone:IMobileTelephone
{
//返回5V
public int GetVoltage()
{
//code
}
}
//M记手机
public class MotorolaMobileTelephone:IMobileTelephone
{
//返回3V
public int GetVoltage()
{
//code
}
}
一直不是很懂,请指导一下,谢谢。
@finer
我大概理解了你的意思。
其实,在此强调的通用是基于对软件设计中抽象的一种直白的表达。正像你写的示例一样,假设商场中有一个万能充电器MobileCharger,那么它的充电操作可能是这样实现的:
public class MobileCharger
{
public void ChangeForPhone(IMobileTelephone phone)
{
phone.GetVoltage();
//Do else.
}
}
正是有了IMobileTelephone接口的抽象,MobileCharger的ChangeForPhone才能有效的“接受”所有实现了IMobileTelephone接口的手机NokiaMobileTelephone、MotorolaMobileTelephone等等。
类比现实中的实例,你不是也在商场中为不同的手机充电吗,只是插头不同而已,但是对充电器类说,这种“通用”已经足有意义了,反馈回程序设计,正是这种抽象才使得MobileCharger有了通用的魔力:-)
你的书,很不错```前五章看了三遍,受益无穷啊`````谢谢啦~~~
--引用--------------------------------------------------
xiaowy: 老实说的确总结得不错,但是一看这标题,心中极为不爽!
<br>什么叫 “你必须知道的.NET“ ,为什么我要必须知道?知道了就一定对我有好处吗?
<br>我也是一名搞技术的
<br>
--------------------------------------------------------
你整一欠抽!!
最看不惯你这种跳梁小丑
王哥 敢问您今天多大了 我23了 我不知道要奋斗多少年才能达到您的高度
我也很迷惑 真的
很仔细的看了你的文章,受益匪浅,谢谢。
顺便问个问题: 《C#高级编程》(第四版) 这本书感觉怎么样? 有必要从头到尾仔仔细细的研读一遍吗? 期待各位的指点:)
@toplee
谢谢你的阅读。
关于《C#高级编程》堪称.NET的红宝书,至少值得推荐。关于从头到尾仔细研读,可能不是我的习惯,我认为对于技术专注大部分的书籍不值得通读,而关注专读,取己所需即可,尤其是这种重量级选手我从来没法全读,拿着都重:-)
我有一个问题,MSDN当中对接口的定义好像没有涉及到“虚”和“抽象”这两个概念,虚方法必须是以virtual来修饰,而抽象一定要以“abstract”来修饰,而且有很多人说重写接口,接口没有重写这种情况吧,最多就是显示接口调用,那是重写吗??
还有,我认为接口只是一些声明,不应该叫它们虚拟成员,而且重写必须要override,这个东西真的是。。。。
接口意义的精髓在于就在一句话:接口名称 对象名 = new 实现类名;而关键中的关键就在一定要用接口名称进行实例化。
@jay-c
--引用--------------------------------------------------
jay-c: 我有一个问题,MSDN当中对接口的定义好像没有涉及到“虚”和“抽象”这两个概念,虚方法必须是以virtual来修饰,而抽象一定要以“abstract”来修饰,而且有很多人说重写接口,接口没有重写这种情况吧,最多就是显示接口调用,那是重写吗??
--------------------------------------------------------
很高兴收到你这么多的回复,因为最近外出,没能及时回复,还望见凉。
关于“虚”和“抽象”的概念,你认为什么是“虚”?什么是“抽象”,说实话我从来没有觉得MSDN就一定是正确的,或者本质的。对于接口而言,不过是否显式修饰了virtual,它本质上都是“虚”的,所以任何对接口的实现在本质上都是覆写,而不管是显式的还是隐式的。
@jay-c
--引用--------------------------------------------------
jay-c: 还有,我认为接口只是一些声明,不应该叫它们虚拟成员,而且重写必须要override,这个东西真的是。。。。
--------------------------------------------------------
如果非要指教概念上的准确性,我想override被称为“覆写”更为合适。至于“虚拟成员”的说法,我倒认为接口成员更合乎方法签名的说法。
@jay-c
--引用--------------------------------------------------
jay-c: 接口意义的精髓在于就在一句话:接口名称 对象名 = new 实现类名;而关键中的关键就在一定要用接口名称进行实例化。
--------------------------------------------------------
关于接口意义的精髓在于就在一句话:接口名称 对象名 = new 实现类名;
这句话,我实在不敢苟同,因为那实在不够揭示接口本质的意义。
接口本质的意义在于约定、契约和规则,接口解决了面向对象中继承所带来的纵向问题,接口实现了更高层次的抽象,这是接口的意义,而“接口名称 对象名 = new 实现类名”只能算是一种应用的表现而非精髓,你举例的应用体现的恰恰是多态的精神,而非接口的精神。
我倒赞赏懒虫对接口的理解。
仅仅是一点己见,欢迎随时讨论,这是非常棒的体验。
一晚深夜,一个作家闲来无事,忽然兴起,要写本小说。一想好小说的主线路,于是就列好提纲,列好后累了,然后就睡觉去了。所以,就留下了接口,等想起哪天要写时就继续去写。
一晚深夜,一个作家闲来无事,忽然兴起,要写本小说。一想好小说的主线路,于是就列好提纲,列好后精神很好,就继续写了小说上的内容,东写西写,然后就睡觉去了。所以,就留下了抽象类,等想起哪天要写时就继续去写。
@X_JKBofer
虽然只是关于这二者的一个方面的特性描述,但是
这是我听过的最动人的关于抽象类和接口的故事,谢谢分享:-)
接口实现回调:
回调多个对象或多种类型的对象 要怎么实现...
大致看完了,总结下对不对:
1、接口比较适合定义关系比较清晰的方法,而且一旦定义就不能再进行修改并且需要完全实现里面定义的方法。
2、抽象类比较设计关系比较复杂的设计(比如存在大量的方法属性或者有共用的方法体实现,并且可能有功能升级方面的需求的)。
不知道理解的对不对,我以前都是用的接口。
学习受用了,我觉得最大的区别是接口着重于CAN-DO关系类型,而抽象类则偏重于IS-A式的关系
@赵俊
呵呵,大部分的情况下,接口是非常受用的。
二者的基本比较在本文已经有很多的陈述,而综合来看我觉得还是回到设计思路上更加的能体会接口和抽象类的区别。
呵呵,你的总结很好。
接口最常用的就是定义规范,很多规范都使用了接口来定义,如Java中的jsp、servlet、ejb等,C#里也有很多。当然,也可以在自己的项目中用接口定义规范,以使项目一致。
但接口有一个缺点,就是不能代码重用。假设有100个类实现同一个接口,而这个接口中的一个方法在这100个类中至少有50个类的实现是完全一样的,那么使用接口就意味会多编写49个同样的方法,代码大量重复。当然,还有另外的方法,就是使用组合的方式(IOC模式更好),但这也要多编写一些代码。而且设计更复杂。最好的方法就是在接口中加入该方法的默认实现,但这和接口的定义相冲突,接口中不能有具体的实现代码。但在接口中加入实现代码,在实现该接口的子类中继承(也可覆盖)的确是一个非常好的方法。于是面向对象语言的设计者不得不加入了一个新的面向对象元素。这个新元素的目的有如下两个:
1. 也同样可起到定义规范的作用
2. 可以有默认的方法实现
第1点与接口相同,接口就意味着抽象(用来定义抽象规则)。而第2点是类的行为。因此,这些专家们就给这个新的面向对象元素起名为抽象类,含义就是同时兼有接口和类的特性(除了实例化外)。在很多规范中也使用了抽象类来定义规则,因此这些规范需要提供一些默认的实现。我们也可以将接口、抽象类、类看成是一软件中的滑动控件,当滑杆拉到最左边,也就是抽象类中没有任何的默认实现,就是接口,而将滑杆拉到最右边,抽象类中所有的方法都有默认的实现,就类似于类(这里并不等于类,因为抽象类无法实例化,但除了实例化外,其他和普通类已经完全一样了)。如果滑杆在中间的任何位置,就是普通意义的抽象类(至少有一个抽象方法需要子类实现,除非子类也是抽象类)。
另外楼主说"接口可以用于支持回调;抽象类不能实现回调,因为继承不支持。 ",不知楼主说的回调是不是将接口作为参数类型,而在方法中对接口的实现方法进行调用,与IOC模式类似。那么抽象类为什么不行呢,抽象类可是接口和类的联合体,除了实例化,可以完全取代类和接口。麻烦楼主解释一下。还有什么叫”因为继承不支持“。
@银河使者
哈哈,真是个有趣的说法:将接口、抽象类、类看成是一软件中的滑动控件。确实是件有意思的事情,不过除了功能上体现为一种滑动偏向外,在面向对象思想上接口和抽象类的定位还确实不能持简单的一统说法,我想这方面的论调已经很多了,也不需要太多语言上的描述了。
呵呵,另外关于”因为继承不支持”,可以详细参考本文第86、87和88楼的讨论,相信已经给出了答案:-)
谢谢你的精彩讨论,值得思考:-)
@Anytao
看了楼主的解释,原来是为了给类节省这唯一的一次继承的机会。 但这只说明使用接口实现回调或多态更好,而并不是说抽象类不支持,从技术上讲,抽象类完全支持回调或多态,只是不建议使用,就象goto语句,虽然可以使用,但在程序中并不建议过多地使用,但某些特殊情况,还是可以用一下地。
楼主说的“抽象类不能实现回调,因为继承不支持。”感觉就彻底否定了抽象类和回调的关系。有些太绝对了,如果说“抽象类虽然也支持回调,但由于类只支持单继承,并不建议使用”可能更好些。
我们也可以将抽象类看作是接口和类所生的child,抽象类继承了接口和类的部分特性,如抽象方法必须实现(接口),不能实例化(接口),单继承(类),可以有默认的方法实现(类),但又不完全是接口和类。
个人认为抽象类的存在就是为了弥补接口和类的不足,接口虽然可以定义规范,但不能重用,而类虽然可以重用,但又就能定义规范,因此,需要有一个既能定义规范,又能重用的面象对象元素,这就诞生了抽象类。
当在差异较大的对象间寻求功能上的共性时,使用接口;当在共性较多的对象间寻求功能上的差异时,使用抽象基类。
抽象类是提取具体类的公因式,而接口是为了将一些不相关的类“杂凑”成一个共同的群体。至于他们在各个语言中的句法,语言细节并不是我关心的重点。
太精辟了!
@银河使者
--------------------------------------------
楼主说的“抽象类不能实现回调,因为继承不支持。”感觉就彻底否定了抽象类和回调的关系。有些太绝对了,如果说“抽象类虽然也支持回调,但由于类只支持单继承,并不建议使用”可能更好些。
--------------------------------------------
好像这个说法更确切,我再推敲推敲。
关于抽象类的存在就是为了弥补接口和类的不足,我倒不这么认为,相反我觉得本来就应该存在三种状态:
1 只有定义,没有实现;
2 没有定义,只有实现;
3 既有定义,又有实现。
这样才是事情的全部可能,不是吗?
3.3 规则与场合
应该是抽象类可以使用public、protected、internal 和private修饰符
笔误吧
@Artech认为:所代码共用和可扩展性考虑,尽量使用Abstract Class。当然接口在其他方面的优势,我认为也不可忽视。
我认为这点有出路,设计模式原则:优先使用对象组全,而不是类继承
@龙潜冰风悄林
多组合,少继承是个基本原则,但要视具体情况而言,也不可一概而论。
组合对变化的隔离是弱依赖的,但是继承的复用机制为子类别提供了更加省力的方式,还是回到具体讨论更好。
--引用--------------------------------------------------
feisky: 学习受用了,我觉得最大的区别是接口着重于CAN-DO关系类型,而抽象类则偏重于IS-A式的关系
--------------------------------------------------------
非常同意。
抽象类和接口结合起来一起用,会带来设计上的本质变化。那感觉真的很爽!
LZ这篇文章写的不错,赞一个啦!
@Anytao
原来这里“回调机制”跟 C++ 的消息机制 是同根同源的。一语惊醒梦中人,学习了!
CLR允许接口可以包含事件、属性、索引器、静态方法、静态字段、静态构造函数以及常数。但是注意:C#中不能包含任何静态成员.
=============================================》
请问这句话是否表述有问题,我没看明白。静态字段,静态构造函数不是静态成员吗?
据我所知 接口可以包含属性、方法、索引指示器和事件,但不能包含常量、域、操作符、构造函数和析构函数,而且也不能包含任何静态成员 。
请指教,谢谢!~
接口名称前面的大写字母“I”是一个约定,正如字段名以下划线开头一样,请坚持这些原则。
----------------------------------------------------
这个约定确实有很多好处
但自己觉得大可不必以 "书写方式" 来区分接口或抽象类, 不必用下划线开头区分字段或局部变量. 这几乎没有多少道理. 当某一天需要将整个接口当做抽象类来转化时, 却不的不为这些过时的约定花费很大的代价更改命名空间和其他的东西. 一个好的编程风格不应该局限于这些东西,以要表达的意义或行为来命名类或接口或字段才是必须坚持的原则, 智能感应可以告诉你那个东西到底是接口还是类.是字段还是抽象类.
另, 我也买了这本书, 确实很好.
@小猴子
所言非虚,你一定研究过EntLib,那里面可尽是这种配合的绝妙实践。
@WalkBy
回调体现在很多方面,其实我还想对回调综合的写一篇由历史延伸而来的技术脉搏,消息机制、接口、委托、事件,甚至WCF中多有很多关于“回调”的故事。
@spiderman
对,确实是没错的,对于事件、属性、索引器、静态方法、静态字段、静态构造函数以及常数在接口中的包含,是CLR规范所允许的,但并不是C#语法所允许的,所以这是两个范畴的问题。
关于这点,你可以了解IL语法相关规范,就有了更明确的答案。
@子逸
呵呵,谢谢你的建议。
关于命名规则和代码规范,我想这些所谓的规则已经是千锤百炼延续而来的传统了,其实规范并不完全是给自己看的,更重要的是目标明确的规范是团队合作的基础。
:-)
接口可以有静态字段、静态构造函数以及常数。
但是接口定义不是说,不可以有字段吗?
这岂不是矛盾,我试验在接口中添加常数好像不可以。编译不过啊。
楼主何解?
小问题,
什么是接口示例中,实现的CompareTo方法不是任何时候都有返回值
很早就了解了您出的这本书,一直没有时间静下来好好完整的看看,现在工作不太忙,就来学习一下!
C#中,对于接口有这个规定:
不能直接实例化接口。
但是msdn中的例子里,这一段我不明白:
public static void Main(string[] args)
{
MyClassWithCallback mc = new MyClassWithCallback();
//********************************
IMyInterface mi = new MyBaseClass();
//********************************
mc.AddCallback(mi);
mc.DoSomething();
mc.RemoveCallback();
}
}
*号包起来的这一句,该作何理解?这不是实例化了么?
问的比较菜,不过还是想闹明白。
@哼哼猪
这里并没有"实例化接口"。
这里是实例化 “MyBaseClass" 类
这样才叫实例化接口,当然这是错误的示范。
IMyInterface mi = new IMyInterface();
注释应该是“覆写(重写)抽象方法”和“覆写(重写)抽象属性”吧!
private int _value;
public int Value
{
get { return _value; }
set { _value = value; }
}
public int CompareTo(object o)
{
//使用as模式进行转型判断
TestCls aCls = o as TestCls;
if (aCls != null)
{
//实现抽象方法
return _value.CompareTo(aCls._value);
这个 ._value 不是私有的吗????怎么可以这样aCls._value
当在差异较大的对象间寻求功能上的共性时,使用接口;当在共性较多的对象间寻求功能上的差异时,使用抽象基类。
这句太概况了!!
,CLR允许接口可以包含事件、属性、索引器、静态方法、静态字段、静态构造函数以及常数
这句话,好像错了吧。我在 vs2008里测试了下,接口里,不可以有静态构造函数。
LZ,《你必须知道的.NET》这本书缺货了?当当网 上缺货了哦
你的TestCls的code有错,CompareTo里面不是所有的code path都返回了值,如果aCls是null的话,就没有返回值。。
@AnyTao
•接口定义可以使用public、protected、internal 和private修饰符,但是几乎所有的接口都定义为public,原因就不必多说了。
感觉这句话很别扭,是不是抽象类可以使用public, protected...?
谢谢楼主好文章,请问
•接口可以用于支持回调;抽象类不能实现回调,因为继承不支持。
回调是啥意思哩?谢谢
lz有一个地方不明白
//声明抽象属性
public abstract string Name
{
get;
}
这里声明的抽象属性有何用,后面的实现好像根本没用到Name属性,而是直接给_name字段赋值了
在不同点的第2点, 抽象类中已实现的成员是指什么呢 楼主
汗,之前对于接口和抽象类一直搞不明白,看了这篇文章之后,终于有点眉目了。只是可能还需要多看几次。呵呵。谢谢楼主喽。嘿嘿
个人觉得面向接口,或者面向抽象编程一个最大的好处,就是可以脱离具体实现来来编程,从而大大的增强了代码的可扩展性。
版本的控制放着这里讨论总感觉不大合适,个人理解版本的控制本来就不是抽象类或者接口的责任范围。
非常感谢!同楼上,对于版本控制的那段可否给予更为详尽的解释?