继承

it2022-05-05  133

 

Person zsPerson = new Chinese();

 

 

继承

前言:在本篇的博客中,我们会讲解继承的类型、实现继承、访问修饰符、接口。继承时我们本次的主题,我们将讨论c#和.Net Framwork如何处理继承。

一:继承的类型:

1.1:实现继承和接口继承:

在面向对象的编程中,有两种截然不同的继承类型:实现继承和接口继承。

实现继承:表示一个类型派生于基类型。它拥有该基类型的所有成员字段和函数。在实现继承中,派生类型采用基类型中每个函数的实现代码,除非在派生类型的定义中指定重写某个函数的实现代码。在需要给现有的类型添加功能。或许多相关的类型共享一组重要的公共功能时,这种类型的继承非常有用。

接口继承:表示一个类型只继承了函数的签名。没有继承任何实现代码。在需要指定该类型具有某些可用的特性时,最好使用这种类型的继承。 

1.2:多重继承:

c#不支持多重实现继承。而c#又允许类型派生自多个接口——多重接口继承。这说明,c#类可以派生自另一个类和任意多个接口。更准确的说:因为System.Object是一个公共的基类,所以每个c#(除了Object类之外)都有一个基类,还可以有任意多个基接口。 

1.3:结构和类:

使用结构的一个限制是结构不支持继承,但每个结构都自动的派生于System.ValueType。不能编码实现类型层次的结构。但接口可以实现接口。换言之:结构并不支持继承,但是支持接口继承。 

结构总是派生自System.ValueType,他们还可以派生自任意多个接口。

类总是派生于System.Object或用户选择的另一个类,他们还可以派生自任意多个接口。

二:实现继承:

如果要声明自另一个类的一个类,就可以使用下面的语法:

 

public class Chinese : Person { }

 

如果类(或结构)也派生自接口,则用逗号分隔列表中的基类和接口:

public class Chinese : Person, IInterface1, IInterface2 { }

如果在类定义中没有指定基类,c#编译器就假定System.Object是基类。因此下面的两段代码生成相同的结果:

 

public class Chinese : object { }

 

public class Chinese { }

如果要引用Object类,就可以使用object关键字,智能编译器会识别它,因此便于编辑代码。

2.1:虚方法:把一个基类函数声明为virtual,就可以在任何派生类中重写该函数:

 

public virtual string VirtualMathod() { return "The method is virtual and defined in MyBaseClass"; }

 

c#中虚函数的概念与标准OOP的概念相同:可以在派生类中重写虚函数。在调用方法时,会调用该类对象的合适方法。在c#中,函数在默认的情况下不是虚拟的,但(除了构造函数以外)可以显示的声明virtual。除非显示指定,否则函数就不是虚拟的。因此在c#要求在派生类的函数重写另一个函数时,要使用override关键字显示的声明:

 

public override string VirtualMathod() { return "This method is an override defined in MyDerivedClass"; }

成员字段和静态函数都不能声明为virtual,因为这个概念只对类中的实例函数成员有意义。 

 

三:抽象类和抽象函数:

c#允许把类和函数声明为abstract。抽象类不能被实例化。而抽象函数不能直接实现。必须在非抽象的派生类中重写。显然,抽象函数本身也是虚拟的(尽管也不需要提供virtual关键字,实际上,如果提供关键字会产生一个编译的错误)。如果类包含抽象函数,则类也应该是抽象的。

 

public abstract string VirtualMathod();//abstract method

 

四:密封类和密封方法:

c#允许把类和方法声明为sealed。对于类,这表示不能继承该类,对于方法,这表示不能重写该方法。 

要在方法或者属性上使用sealed关键字,必须先从基类上把它声明为要重写的方法或者属性。如果基类上不希望有重写的方法或者属性,就不要把它声明为virtual。

五:派生类的构造函数:

在创建派生了类的实例的时候,实际上会有多个构造函数起作用。要实例化的类的构造函数本身不能初始化类,还必须调用基类中的构造函数。

public abstract class Person { private string _name; public string Name { get { return _name; } set { _name = value; } } }

 

public class Chinese { private int _age; }

客户端的调用:

Person zsPerson = new Chinese();

显然,成员字段name和age都必须在实例zsPerson时进行初始化。如果没有提供自己的构造函数,而是仅依赖于默认的构造函数,那么name会初始化为null引用。age初始化为0.这就有关于我们构造函数的执行的顺序了。

我们首先来理解一下构造函数的执行的顺序:假定默认的构造函数一直在使用:编译器首先找到视图实例化的类的构造函数,在本类中是Chinese,这个默认的Chinese构造函数为其直接基类Person运行默认的构造函数。然后Person构造函数为其直接基类System.Object运行默认的构造函数。System.Object没有任何的基类,所有他的构造函数执行。并把控制权返给Perso构造函数,现在执行的是Person构造函数,把name初始化为null,在把控制权返回给Chinese构造函数,接着执行这个构造函数,把age初始化为0,并且退出。此时,Chinese实例就已经成功了构造和初始化了。

我们注意构造函数的执行的顺序:最先调用的是总是基类的构造函数。先调用System.Object,在按照层次的结构由上到下进行。直到到达编译器要实例化的类为止。每个构造函数都初始化自己类中的字段。

六:在层次结构中添加带参数的构造函数:

首先是带一个参数的Person构造函数,它仅在提供其姓名时才实例化:

 

private string _name; public string Name { get { return _name; } set { _name = value; } } protected Person(string name) { this.Name = name; }

 

在编译器试图为派生类创建默认的构造函数的时候,会产生一个编译错误,因为编译器为Chinese生成的默认构造函数视图调用一个无参数的Person构造函数。但是Chinese没有这样的构造函数。因此,需要为派生类提供一个构造函数,来避免这个错误(使用base调用父类的构造函数)。

public class Chinese : Person { public Chinese(string name) : base(name) { } }

所以当我们new对象的时候会执行我们的构造函数,进行初始化赋值的操作。

 

Person zsPerson = new Chinese("张三");

 

我们接下里做一个练习,首相我们声明一个Person,里面有我们的数据成员,包括名字、年龄、性别。我们自己声明的构造函数进行对字段的初始化赋值。

public class Person { private string _name; public string Name { get { return _name; } set { _name = value; } } private int _age; public int Age { get { return _age; } set { _age = value; } } private char _gender; public char Gender { get { return _gender; } set { _gender = value; } } public Person(string name, int age, char gender) { this.Name = name; this.Age = age; this.Gender = gender; } }

子类的声明,继承父类的构造函数(因为编辑器默认调用时父类的无参数的构造函数,但是它没有了),所以需要显示的调用父类有参数的构造函数。子类也可以有自己的数据成员,接下来我们声明一个程序员类继承父类Person:

//程序员类 public class Programmer : Person { //工作时间 private int _workYear; public int WorkYear { get { return _workYear; } set { _workYear = value; } } public void ProgrammerSayHello() { Console.WriteLine("我叫{0},我是一名程序猿,我是{1}生,我今年{2}岁了,我的工作年限是{3}年",this.Name,this.Gender,this.Age,this.WorkYear); } public Programmer(string name, int age, char gender, int workYear) : base(name, age, gender) { this.WorkYear = workYear; } }

客户段程序的调用:

Programmer pro = new Programmer("程序猿", 23, '', 3); pro.ProgrammerSayHello(); Console.ReadKey();

 

转载于:https://www.cnblogs.com/MoRanQianXiao/p/7805502.html


最新回复(0)