对于我这种从C++中转过来的人来说,已经相当熟悉C++中的继承特性了。刚开始接触C#的继承时,写的继承类的代码基本就和C++的一样。所以记录以下东西来加深对C#继承的。对于C++来说,用的最多的是实现继承,但是对于C#而言,还有一个接口继承。先来说说实现继承和接口继承。
实现继承:便是一个类型派生于一个基类型,拥有该基类型的多有成员字段和函数。在实现继承中,派生类型的每个函数采用基类型的实现代码,除非在派生类型的定义中指定重写该函数的实现代码。在需要给现有的类型添加功能,或许多相关的类型共享一组重要的公共功能时,这种类型的继承是非常有效的。
接口继承:表示一个类型只继承了函数的签名,没有继承任何实现代码。在需要指定该类型具有某些可用的特性时,最好使用这种类型的继承。
关于多重继承
C#中不支持多重实现继承。即一个类只能派生于一个基类,但是可以继承于多个接口!
一、实现继承
例如下面就是一个实现继承,相信对于很多熟悉C++的程序员来说是很熟悉的:
public class CFather
{
public CFather()
{
Console.WriteLine("我是基类CFather");
}
//其他成员
}
public class CSon : CFather
{
public CSon()
{
Console.WriteLine("我是CFather的派生类!");
}
//其他成员
}
注意:C#不支持私有继承,因此基类名上没有public或private限定符!
如果类也派生于接口,则用逗号分隔开基类和接口:
public class CSon : CFather, Interface1, Interface2
{
//成员实现
}
虚方法
把一个基类函数声明为virtual,该函数就可以在任何派生类中重写了。这同样也适用于属性。
C#中可以在派生类中重写虚函数。在调用方法时,会调用对象类型的何时方法。在C#中,函数默认情况下不是虚拟的,但(除了构造函数以外)可以显式的声明为virtual。但是C#要求在派生类的函数重写另一个函数时,要用override关键字显式声明。下面来看一个例子:
public class CFather
{
public CFather()
{
Console.WriteLine("我是基类CFather");
}
virtual public void Fuction()
{
Console.WriteLine("Fuction in CFather!");
}
}
public class CSon : CFather
{
public CSon()
{
Console.WriteLine("我是CFather的派生类!");
}
public override void Fuction()
{
Console.WriteLine("Function in CSon!");
}
}
在Main()函数中:
CFather a = new CFather();
CSon b = new CSon();
a.Fuction();
b.Fuction();
运行结果如下:
我是基类CFather
我是基类CFather
我是CFather的派生类!
Fuction in CFather!
Function in CSon!
派生类的构造函数
构造函数的调用顺序是先调用System.Object,再按照层次节后由上向下进行,直到到达编译器要实例化的类为止。还要注意在这个过程中,每个构造函数都初始化自己的类中的字段。
构造函数的执行顺序,总是最先调用基类的构造函数。也就是说,派生类的构造函数可以在执行过程中调用基类方法、属性和其他成员,因为基类已经构造出来了,其字段也初始化了。
可以看到有两个“我是基类CFather”!这是因为派生类的对象的实例化必须从基类往下一步步的进行,这和C++的是一样的!
注意:成员字段和静态函数都不能声明为virtual,因为这个概念只对类中的实力函数成员有意义。
调用函数的基类版本
C#有一种特殊的语法用于从派生类中调用方法的基类版本:base.<MethodName>()。
抽象类和抽象函数
C#允许把类和函数声明为abstract,抽象类不能实例化,而抽象函数没有执行代码,必须在非抽象的派生类中重写。显然,抽象函数也是虚拟的(但也不需要提供virtual关键字,实际上如果提供了该关键字,就会产生一个语法错误)。如果类包含抽象函数,该类将也是抽象的,也必须声明为抽象的。当然抽象类里也可以声明变量:
abstract class CAbstract
{
public abstract void Fuction1(int a);
}
class CSon : CAbstract
{
public CSon()
{
}
public override void Fuction1(int a)
{
Console.WriteLine("{0}", a);
}
}
密封类和密封方法
C#允许把类和方法声明为sealed。对于类来说,这表示不能继承该类;对于方法来说,这表示不能重写该方法。例如:
sealed class ClassA
{
//方法属性等……
}
class ClassB : ClassA//这样的继承是错误的!
{
//方法属性等……
}
二、修饰符
可见性修饰符:
其他修饰符:
C#中的接口
接口是把隐式公共方法和属性组合起来,以封装特定功能的一个集合。一旦定义了接口,就可以在类中实现它。这样,类就可以支持接口所制定的所有属性和成员、
注意,接口不能单独存在。不能实例化一个类那样实例化接口,而且接口的成员都是公共的。另外,接口不能包含其实现其成员的任何代码,而只能定义成员本身。实现过程必须在实现接口的类中实现。接口不能包含字段、构造函数、析构函数、静态成员或常量!
一个类可以支持多个接口,多个类也可以支持相同的接口。所以接口的概念让用户和其他开发人员更容易理解其他人的代码。例如有一些代码使用一个带某接口的对象。假定不使用这个对象的其他属性和方法,就可以用领个对象代替这个对象。
接口的声明
//声明一个接口IA:
interface IA
{
int Max_int(int a, int b);
}
接口的实现
由于接口必须在类中实现,所以下面我们定义一个CInstance类:
class CInstance : IA
{
public CInstance()
{
}
public int Max_int(int a, int b)
{
if (a >= b)
return a;
else
return b;
}
}
如果一个类继承于类又继承于接口,那么就用逗号来区分他们。接口的继承是可以实现多重继承的。
注意:对接口的继承一定要在类中实现接口中的所有方法!不如不会通过编译的!