继承
在面向对象编程中,有两种截然不同的继承类型,实现继承和接口继承;C#中不支持多重继承,C#类可以派生自另一个类和任意多的接口
- 实现继承:表示一个类型派生自一个基类型,它拥有该基类型的所有成员字段和函数,在需要给现有类型添加功能或者许多相关类型共享一组重要的公共功能时.这种类型继承非常有用
- 接口继承:表示一个类型只继承了函数的签名,没有继承任何的实现代码
实现继承virtual/override
/// <summary> /// 基类 /// </summary> class Person { /// <summary> /// 使用virtual关键字定义的方法允许在派生类中使用override重写 /// </summary> public virtual void SayHello() { Console.WriteLine("基类的SayHello"); } } /// <summary> /// 派生自Person /// </summary> class ChinaPerson : Person { /// <summary> /// 使用override关键字重写基类的SayHello方法 /// </summary> public override void SayHello() { Console.WriteLine("你好"); } } /// <summary> /// 派生自Person /// </summary> class ThailandPerson : Person { public override void SayHello() { Console.WriteLine("萨瓦迪卡"); } }
把一个基类函数声明为virtual,就可以在任何派生类中重写该函数,virtual也适用于属性;需要注意:成员字段和静态函数都不能声明为virtual,因为这个概念只对类中的实例成员有意义
隐藏方法
如果签名相同的方法在基类和派生类中都进行了声明,但该方法没有声明为virtual和override,派生类会隐藏基类方法
class Person { public void SayHello() { Console.WriteLine("基类的SayHello"); } } class ChinaPerson : Person { //提示:隐藏继承的成员Person.SayHello,如果有意的,请使用关键字new public void SayHello() { Console.WriteLine("你好"); } }
修改后
class Person { public void SayHello() { Console.WriteLine("基类的SayHello"); } } class ChinaPerson : Person { public new void SayHello() { Console.WriteLine("你好"); } }
调用函数的基类版本
C#中可以使用base.<MethodName>()这种语法来调用方法的基类版本
class Person { public virtual void SayHello() { Console.WriteLine("基类的SayHello"); } } class ChinaPerson : Person { public override void SayHello() { base.SayHello(); Console.WriteLine("你好"); } }
抽象类和抽象函数
C#中允许把类或函数声明为abstract,抽象类不能被实例化.抽象函数也不能直接实现,必须在非抽象的派生类中重写
- 如果类包含抽象函数,则该类也必须被声明为抽象的
- 抽象方法只在派生类中真正实现,这表明抽象方法只存放函数原型,不涉及主体代码
- 派生自抽象类的类需要实现其基类的抽象方法,才能实例化对象
- 使用override关键子可在派生类中实现抽象方法,经override声明重写的方法称为重写基类方法,其签名必须与override方法的签名相同
密封类和密封方法
C#允许把类或方法声明为sealed,对于类而言,这表示不能继承该类,对于方法而言,这表示不能重写该方法
派生类的构造函数执行
- 默认构造函数,如果没有为类指定任何构造函数,编译器会自动为类创建一个无参构造函数,用以初始化类的字段;如果为类编写了构造函数,那么编译器就不会再自动生成无参构造函数了。ps.C#不允许用户为结构定义无参构造函数。
- 静态构造函数,不能访问实例成员,只能用来初始化一些静态字段或者属性,仅在第一次调用类的任何成员时自动执行,不带访问修饰符,不带任何参数,每个类只能有一个静态构造函数,但可以同时还有一个无参实例构造函数
- 私有构造函数,将构造函数申明为私有的,则不能通过new运算符在外部代码中实例化(但可以编写一个静态方法或属性在其内部实例化一个对象,再将结果返回给外部代码)。私有构造函数的作用:永远不会被实例化,仅作为某些静态成员的容器/希望类智能通过某个静态方法来实例化(对象实例化的类工厂)
class Program { static void Main(string[] args) { ChinaPerson chinaPerson = new ChinaPerson(); Console.ReadKey(); } } class Person { static Person() { Console.WriteLine("Person静态构造函数"); } public Person() { Console.WriteLine("Person实例构造函数"); } } class ChinaPerson : Person { static ChinaPerson() { Console.WriteLine("ChinaPerson静态构造函数"); } public ChinaPerson() { Console.WriteLine("ChinaPerson实例构造函数"); } }
输出
ChinaPerson静态构造函数
Person静态构造函数
Person实例构造函数
ChinaPerson实例构造函数
构造函数执行顺序为:调用System.Object.Object()->ChinaPerson被实例化所以调用自身的静态构造函数->跳到ChinaPerson的实例构造函数发现基类Person->Person类被调用,所以调用自身的Person静态构造函数->Person实例无参构造函数->:ChinaPerson实例无参构造函数(先调用System.Object的构造函数,再按照继承的层次结构从上往下进行,直到最终要实例化的类为止)
可以使用base关键字来实现不同构造函数的调用
接口继承
指定一组函数成员而不实现成员的引用类型,其它类型和接口可以继承接口
接口继承中又分为隐式实现和显式实现
interface IPerson { void SayHello(); } /// <summary> /// 隐式实现接口 /// </summary> class ChinaPerson : IPerson { public void SayHello() { Console.WriteLine("你好"); } } /// <summary> /// 显式实现接口 /// </summary> class ThailandPerson : IPerson { void IPerson.SayHello() { Console.WriteLine("莎娃迪卡"); } }
对于隐式实现的接口调用这两种方式都可以
ChinaPerson chinaPerson = new ChinaPerson(); IPerson person = new ChinaPerson(); person.SayHello(); chinaPerson.SayHello();
对于显式实现的接口调用:只能使用接口调用
IPerson thailandPerson = new ThailandPerson(); thailandPerson.SayHello();
抽象类和接口的区别
相同点:
- 都可以被继承
- 都不能被实例化
- 都包含方法声明
- 派生类必须实现未实现的方法
区别:
抽象基类可以定义字段/属性/方法实现.接口只能定义属性/索引器/事件/方法声明
抽象类是一个不完整的类,需要通过集成进一步细化.而接口更像是一个行为规范,表名我能做什么
接口是可以被多重实现的,可以有多个类实现接口,因为类的单一继承性,抽象类只能被单一继承
抽象类实现继承需要使用override关键字,接口则不用
如果抽象类实现接口,可以把接口方法映射到抽象类中作为抽象方法不必实现,而在抽象类的子类中实现接口方法
抽象类表示的是,这个对象时什么;接口表示都是,这个对象能做什么;使用抽象类是为了代码的复用,使用接口是为了实现多态性
普通类和抽象类的区别
相同点:
- 都可以被继承
- 抽象类不能实例化,普通类允许实例化
- 抽象方法值包含方法声明而且必须包含在抽象类中
- 子类继承抽象类必须实现抽象类中的抽象方法除非子类也是抽象类
- 抽象类中可以包含抽象方法和实例方法
原文地址:https://www.cnblogs.com/GnailGnepGnaw/p/10603401.html