转自:http://www.soaspx.com/dotnet/csharp/csharp_20091030_1359.html
一、接口
接口是C#中很常见的工具,概念什么的就不说了,这里讲几个值得注意的小地方:
1、接口内部只能有函数、属性和事件的声明:
{
void Show();
string Type //--属性的声明
{
get ; //-- get; 不能为get{};或是:get();
set ;
}
event AddChildren Add;
}
在接口中声明的成员都不需要访问修饰符(public,private等),因为接口成员的权限默认都是public,另外值得注意的是接口中之所以能够声明事件是因为事件就是委托的特殊属性。
接口不能是静态的,接口成员也只能声明为实例成员不能声明为静态成员,如下声明就是错误的:
{
static void Show();
string Type
{
get ;
set ;
}
event AddChildren Add;
}
你会得到提示:
错误 1 修饰符“static”对该项无效 C:\Documents and Settings\HMD\桌面\CLR_Excise\CLR_Excise\Lib.cs 10 22 CLR_Excise
原因是如果接口是静态的就无法被继承,静态成员也无法被接口直接访问(因为接口中的成员都还没有实体只是声明)所以定义在接口中无意义。
2、接口支持部分声明:
上面的接口实际上可以在一个命名空间里声明为3部分:
{
void Show();
}
partial interface IParent
{
string Type
{
get ;
set ;
}
}
partial interface IParent
{
event AddChildren Add;
}
上面这种部分接口的声明方式和声明为一个接口的效果完全相同,若要了解部分关键字partial请查看:C#里partial关键字的作用(转摘)
3、接口的实现:
接口成员的实现方式分为两种:
<1>隐式实现:
{
#region IParent 成员
public void Show()
{
throw new NotImplementedException();
}
public string Type
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public event AddChildren Add;
#endregion
}
在类中隐式声明的接口成员都必须是public访问权限
(2)如果有一个类C实现了两个接口IA、IB且IA、IB都有同名函数void Display();那么可以在C中隐式实现Display,这样即实现了IA的Dislpay也实现了IB的Display
例如:
public interface IParent
{
void Show();
string Type
{
get ;
set ;
}
event AddChildren Add;
}
public interface IFather
{
void Show();
string Type
{
get ;
set ;
}
event AddChildren Add;
}
public class ChildrenA : IParent, IFather
{
#region IParent,IFather 成员
public void Show()
{
throw new NotImplementedException();
}
public string Type
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public event AddChildren Add;
#endregion
}
在上面类CA中将IParent和IFather的同名成员都用隐式实现接口成员的方式一次性实现了。
(3)隐式实现接口属性的时候可以扩充接口属性的访问器,例如:
{
string Type
{
get ;
}
}
public class CParent : IParent
{
#region IParent 成员
public string Type
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
#endregion
}
本来接口属性Type只有get访问器,但是实现接口的类CParent扩充了接口属性Type即定义了get访问器也定义了set访问器,这在隐式实现接口属性的时候是允许的,但是在显式实现接口属性的时候必须严格按照接口定义的格式来!
<2>显式声明接口:
{
void IParent.Show()
{
throw new NotImplementedException();
}
string IParent.Type
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
event AddChildren IParent.Add
{
add { throw new NotImplementedException(); }
remove { throw new NotImplementedException(); }
}
}
显式实现接口不能设置实现成员的访问权限(因为显式声明成员的权限就是其对应接口成员的权限,当然就是public)
此外显式实现接口需要注意以下几点:
(1)显式实现接口,那么实现的接口成员只能由接口调用,因为显式实现的接口成员对于实现类来说是不可见的。
(2)显式实现接事件的时候要注意,只能显示实现事件的定义,例如显式实现IParent.Add事件的时候,用的显式实现事件:
event AddChildren IParent.Add
{
add { throw new NotImplementedException(); }
remove { throw new NotImplementedException(); }
}
如果用隐式实现事件就会报错:
event AddChildren IParent.Add
提示:错误 1 事件的显式接口实现必须使用事件访问器语法 C:\Documents and Settings\HMD\桌面\CLR_Excise\CLR_Excise\Lib.cs 45 38 CLR_Excise
(3)上面隐式实现接口(3)中的例子没有区分IParent、IFather同名成员的实现,而是在继承类CA中由隐式实现方式统一实现。下面举例说明如何将IParent、IFather的同名成员分别实现:
还是有一个类C实现了两个接口IA、IB且IA、IB都有同名函数void Display();,这次为了区分IA,IB同名成员Dispaly函数的实现,可以在CA中将一个接口的Display函数用显式方式实现,这说明在实现接口的时候可以将其中一个接口的同名成员用显式方式实现,以区分同名成员的实现
interface IParent
{
void Show();
string Type
{
get ;
set ;
}
event AddChildren Add;
}
interface IFather
{
void Show();
string Type
{
get ;
set ;
}
event AddChildren Add;
}
class ChildrenA : IParent, IFather
{
#region IParent 成员
public void Show()
{
throw new NotImplementedException();
}
public string Type
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public event AddChildren Add;
#endregion
#region IFather 成员
void IFather.Show()
{
throw new NotImplementedException();
}
string IFather.Type
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
event AddChildren IFather.Add
{
add { throw new NotImplementedException(); }
remove { throw new NotImplementedException(); }
}
#endregion
}
现在就将IParent和IFather接口的同名成员分开实现了,其中将IFather接口的同名成员用显式方式声明以区分IParent接口的实现。事实上可以将IParent和IFather的成员都以显式方式实现,不过这样实现类CA就不能访问到IParent和IFather的成员了,只有用IParent和IFather来访问它们各自的成员。
(4)如果实现类继承了具有同名成员的抽象类,那么加上override关键字后即实现了抽象类也实现了接口
interface IParent
{
void Show();
string Type
{
get ;
set ;
}
event AddChildren Add;
}
abstract class AParent
{
public abstract void Show();
public abstract string Type
{
get ;
set ;
}
public abstract event AddChildren Add;
}
class ChildrenA :AParent, IParent
{
#region AParent、IParent 成员
public override void Show()
{
throw new NotImplementedException();
}
public override string Type
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override event AddChildren Add;
#endregion
}
二、抽象类
抽象类是C#里面用的相对较少的工具,不过在有些场合他比接口更有用,抽象类的作用与接口类似就是声明个框架,让继承类照着这个框架去实现,不过抽象类允许对已经确定的部分先进行实现,继承类要做的就是实现没有确定的成员(抽象成员)
abstract 修饰符可以和类、方法、属性、索引器及事件一起使用。
在使用抽象类的时候需要注意:
(1)抽象类和接口一样是个框架,本身不能被实例化,只能通过继承类将其实例化。
(2)抽象类不能使用sealed和static关键字,因为使用sealed关键字表示类不能被继承,而静态类也不能被继承,抽象类不被继承是完全没有意义的。
(3)实现抽象类的类必须实现抽象类的所有抽象成员(标记了abstract的成员):
{
public abstract void Show();
public abstract string Type
{
get ;
set ;
}
public abstract event AddChildren Add;
public int tInt
{
get
{
return 1;
}
}
}
public class Parent : AParent
{
public override void Show()
{
throw new NotImplementedException();
}
public override string Type
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override event AddChildren Add;
}
Parent类实现了AParent的所有抽象成员,但是并没有实现AParent的非抽象成员public int tInt
(4)抽象成员都是虚成员(virtual),这一点上面的代码中可以看到,实现抽象类的成员都使用了关键字override(实现抽象成员必须要用override)表示重写抽象类的抽象成员
(5)抽象成员不能标记为static(原因同接口)、virtual,且抽象成员不能是private的(原因就是private成员无法被继承)。
(6)抽象类的抽象成员可以重写(override)或隐藏(new)基类成员:
{
public abstract void Call();
}
abstract class ACB : ACA
{
public override abstract void Call();
}
如上所示ACB的抽象成员就重写了父类ACA的抽象成员
(7)抽象类也支持部分定义,例如可以将(3)中的AParent分成3部分定义,其效果和定义成一个相同:
{
public abstract void Show();
public int tInt
{
get
{
return 1 ;
}
}
}
public abstract partial class AParent
{
public abstract string Type
{
get ;
set ;
}
}
public abstract partial class AParent
{
public abstract event AddChildren Add;
}