您的位置:首页 > 其它

浅谈接口与抽象类的区别

2013-09-05 15:42 246 查看
从代码的语法定义和使用逻辑两个方面浅谈接口与抽象类的区别.

1 语法定义篇

(1)首先是定义语法

接口

接口的定义是

[访问修饰符] interface 接口名

{

// 接口成员

}

抽象类

抽象类的定义是

[访问修饰符] abstract class 类名

{

// 类成员

}

定义语法中接口关键字interface,类关键字class没什么差异,抽象类多一个abstract修饰

(2) 成员类型

接口

对于接口,它是定义的一类能力,因此以功能为主,面向一类抽象能力,所以成员只与方法有关. 那么记忆接口能定义什么,就记住方法便可.

举个例子,对于接口成员可以包含:方法、属性、事件和索引器.

实际上可以知道,属性是特殊的方法,事件是一个私有的委托变量加上两个方法,而索引实际上就是属性,因此对于接口成员的记忆,可以归结为“方法”

抽象类

抽象类的成员与一般类成员没什么区别,只不过抽象成员必须在抽象类中. 而在抽象类中一般定义的抽象成员有方法、属性、事件和索引器.

另外,抽象类可以有构造方法,其作用是为非抽象成员进行初始化,而不是作为创建抽象类的实例而使用

注:即使抽象构造方法不能被外界调用,但是也不能设定为private,因为其派生类会默认调用无参构造方法

(3)成员访问修饰

接口

接口就是为了被实现的,即每个成员都是要被别的类进行实现的,那么每个成员都应该是public类型,因此不用设定接口中成员类型的访问修饰符,默认为public,一般语法为

返回类型 方法名(参数列表);

抽象类

抽象成员必须设定为public,其余没有要求

(4)各抽象成员的定义

接口

方法的定义:

返回类型 方法名(参数列表);

属性的定义:

返回类型 属性名{ get; set; }

事件的定义:

event 委托名 事件名;

索引的定义:

返回类型 this[索引类型] { get; set; }

例如:

public interface IMyInterface
{
void Func();
string Value
{
get;
set;
}
event EventHandler MyEventHandler;
string this[int index]
{
get;
set;
}
}


注:索引器与属性的get与set不同于自动属性,是可以选择只读、只写、还是读写的 一般接口都已I命名开始

抽象类

其抽象成员定义类似,但是一般在抽象类中的抽象成员以方法和属性居多.

首先抽象成员可以来自接口获得,只需要在每一个成员的前面加上public abstract即可

例如:

public abstract class MyAbs : IMyInterface
{
public abstract void Func();
public abstract string Value
{
get;
set;
}
public abstract event EventHandler MyEventHandler;
public abstract string this[int index]
{
get;
set;
}
}


其次,抽象类可以来自与某个基类,将基类的虚方法用abstract进行修饰,例如

public class BaseClass
{
public virtual void Func()
{
// 方法体
}
}
public abstract class MyAbs : BaseClass
{
public abstract void Func();
}


注:

1、方法没有方法体是指参数括号写完后没有花括号,直接分号结束;如果写成

public abstract void Func() { }

不叫作无方法体,只能称作空实现,这样会出现语法错误.

2、抽象成员必须是public的,同时由abstract修饰.

3、接口中的事件定义与抽象类中的事件定义意义和方法定义相同:

-> 在接口中定义一个事件,相当于同时定义了add和remove没有方法体的方法

void add_MyEventHandler(EventHandler value);

void remove_EventHandler(EventHandler value);

-> 在抽象类中,相当于两个abstract方法

public abstact void add_MyEventHandler(EventHandler value);

public abstact void remove_EventHandler(EventHandler value);

3、在其子类中可以被实现,对于接口实现add和remove方法,对于抽象类重写它们

(5)抽象成员的实现

实现接口

实现接口的类分三种,一是抽象类,二是一般的类,再就是结构. 而实现方式又分为实现接口和显式实现接口.

抽象类

抽象类实现接口,只需要将接口中的成员与方法添加好就行了,但是必须保证成员的访问级别为public

例如:

public abstract class MyAbs : IMyInterface
{
public void Func()
{
// 实现代码
}
public string Value
{
get;
set;
}
public event EventHandler MyEventHandler;
public string this[int index]
{
get {
// 实现代码
}
set {
// 实现代码
}
}
}


注意:

1、此处一定不允许丢掉public,因为接口就是用来定义方法,最后是要被访问的,如果不设为public,那么会出现问题.

2、此处的属性与接口中的属性意义是不同的,此处为自动属性,在代码的后台编译器会自动生成一个字段,并填补get和set读取器.

3、此处的事件也与接口中的事件意义不同(从代码角度看几乎一样),这里的事件,编译器会自动生成一个同名的私有委托,并将add方法与remove方法补全.

如果不希望实现接口中的方法,可以将接口成员直接copy下来,在前面加上abstract关键字即可. 例如

public abstract class MyAbs : IMyInterface
{
public abstract void Func();
public abstract string Value
{
get;
set;
}
public abstract event EventHandler MyEventHandler;
public abstract string this[int index]
{
get;
set;
}
}


那么此时的各个成员,其本质与接口中的成员一样,只有方法签名,没有实现体.

对于“显式实现接口”,其实现方式就是将成员写成

接口名.成员名

的形式. 显式实现接口与一般的实现区别在于显式实现的成员只有接口对象才能调用.

实现方式代码如下

public abstract class MyAbs : IMyInterface
{
void IMyInterface.Func()
{
// 方法体
}
string IMyInterface.Value
{
get;
set;
}
event EventHandler IMyInterface.MyEventHandler
{
add {
// 实现代码
}
remove {
// 实现代码
}
}
string IMyInterface.this[int index]
{
get
{
// 实现代码
}
set
{
// 实现代码
}
}
}


注意:

1、这里显示实现的接口,不允许有访问修饰符. 因为显式实现的接口只能由接口对象来调用,这里无论写什么都会出现问题.

2、这里事件不允许使用默认系统自定义的方式,需要自己添加add方法和remove方法

3、另外显示实现接口的成员不允许使用abstract进行修饰.

普通类实现接口

对于普通的类实现接口,只需要将接口的成员的代码实现补全即可,这里与抽象类是一样的,同样对于事件,可以手动添加add方法和remove方法.

如果在实现的方法中,有部分需要在子类中被重写,那么只需在方法前加上virtual进行修饰即可,示例代码如下:

public class MyClass : IMyInterface
{
public virtual void Func()
{
// 方法体
}
}


注:

1、这里的成员访问修饰符也必须是public.

2、这里同样可以“显式实现接口”,规则与方法与抽象类中介绍的一致

-> 不允许使用virtual修饰成员

-> 事件必须使用“事件访问器”语法

-> 成员没有访问修饰符

实现接口的除了类以外,接口也可以实现接口

实现抽象类

抽象类的实现与接口实现由点类似,不过这里不存在显式与非显式罢了. 在实现抽象类的过程中,抽象类的每一个抽象成员都需要提供实现代码,因为抽象类中包含没抽象成员,因此继承自抽象类的类,只用提供重写代码即可.

不过在语法中与接口的实现还是有点不一样. 接口中成员的实现与一般的写法是一样的,但是实现抽象类的成员,每一个成员前面都要有override的修饰. 代码如下:

public class MyClass : MyAbs
{
public override void Func()
{
// 实现代码
}
public override string Value
{
get;
set;
}
public override event EventHandler MyEventHandler;
public override string this[int index]
{
set {
// 实现代码
}
get {
// 实现代码
}
}
}


注:

1、这里实现的成员必须使用public override进行修饰.

2、事件可以不自己提供add方法与remove方法

-> 此时编译器会自动添加一个私有的委托变量,以及add方法与remove方法

3、这里必须保证每一个成员都要重新实现,这里示例代码中的属性是自定属性,因此看起来与抽象类中的一样,但是编译器会自己添加私有字段与get方法和get方法.

4、如果依旧不需要某些方法提供方法体,即仍旧是抽象成员,那么需要补全其他方法,将不需要方法提的方法用abstract修饰即可,同时不要忘记类也应该使用abstract进行修饰,因为抽象成员只能允许在抽象类中存在.

属性的实现

属性定义为抽象类型可以由三种形式:只读属性、只写属性和读写属性.

在实现属性的时候,前面提到的例子都是读写属性,下面来看看另外两种.

只读属性

只读属性的定义为:

Random r = new Random();

IMobilePhone[] nks = new IMobilePhone[100];

for(int i = 0; i < nks.Length; i++)

{

if(r.Next(2) == 1)

{

nks[i] = new Nokia1280();

}

else

{

nks[i] = new Nokia_Lumia_900();

}

}

foreach(IMobilePhone n in nks)

{

n.Message();

Console.WriteLine();

}


View Code
注:这里只针对一个Message方法,与什么手机没有任何关系,100元的一般手机可以实现,好几千元的智能手机也能实现,因此不用考虑那么复杂,使用接口变得更加容易. 因为此处面向的是功能,是一种能力.

面向运行软件能力的应用

再来看一个关于智能机应用的例子.

比如我现在要做一个网络交友平台,这个显然是针对智能机设定的一个功能,假定所有手机都已经安装完成客户端,我现在的应用只针对用户发来的数据包,即只负责与用户进行数据的交互. 那么完全没有必要针对所有智能手机这个对象,只需要使用RunApp方法即可,那么此处使用接口是再好不过的了.

综上讨论,可以得出结论了,对于使用逻辑上,接口与抽象类到底用哪一个,在于你所写程序的关注点,你的项目中更关注与功能与方法,那么使用接口比较好;若果是关注一类对象,那就使用抽象类了. 所以说接口是对一类功能的抽象,抽象类是对一类对象的抽象.

同时不要忘记,对于结构的继承关系,只能使用接口.

当然再怎么用,也不会像这样来写代码的,实在是太复杂了.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: