简介编程
继承(封装、多态)是面向对象编程三大特性之一,继承的思想就是摈弃代码的冗余,实现更好的重用性。函数
继承从字面上理解,无外乎让人想到某人继承某人的某些东西,一个给一个拿。这个语义在生活中,就像this
家族继承财产,爷爷将财产继承给儿女,儿女在将财产继承给子孙,有些东西能够继承有些的东西只继承给spa
某人。映射到编程当中,其思想也大体如此。调试
经过示例引出继承的做用code
在代码中定义个三个类:Cat猫、Dog狗、Cattle牛。对象
从类图上能够看出红色标识区域,三个类的定义出现了大量的冗余(字段、属性、方法),那么在编写代码时就会出现大量的重复代码。blog
试想一下,随着业务功能的扩展,可能会出现更多类,那么冗余(重复的代码)会更多。好比出现一样会形成冗余的类:继承
Pig猪、Panda熊猫、Sheep羊......等等。这些类一样会有相同的特征:名称、性别、年龄、奔跑(字段、属性、方法)。get
如何解决此类冗余问题 —— 使用继承
继承的思想:
当咱们定义了多个类,这多个类都存在重复的成员(共性)。咱们能够将这些重复的成员单独的提取封装到一个类中,做为这些具备相同特征类的父类。
将此思想做用于上述的三个类
提取共性:能够直观看出重复的具备共性的项目有:1.字段和属性(年龄、姓名、性别)、2.方法(奔跑)。
封装到一个类:如何定义这个类?Cat猫、Dog狗、Cattle牛有明显共同的特性,就是他们都是动物,故能够抽象定义一个Animal动物类。
如何在代码中实现继承
class Animal { private string name; public string Name { get { return name; } set { name = value; } } private string gender; public string Gender { get { return gender; } set { gender = value; } } private int age; public int Age { get { return age; } set { age = value; } } public void Run() { Console.WriteLine("奔跑。。。"); } } class Cat:Animal { public void CatchMouse() { Console.WriteLine("抓老鼠。。。"); } } class Dog:Animal { public void GuardHouse() { Console.WriteLine("看家护院。。。"); } } class Cattle:Animal { public void Plowland() { Console.WriteLine("耕田。。。"); } }
经过一个简单的 :(冒号)实现了继承关系。
实现继承后产生了两个角色:1.子类(派生类)、2.父类(基类)
代码中子类删除父类提取的重复性成员。
实现继承后的关系以下图:
实现继承后每一个子类仅保留了本身特有的特性,大大减小了冗余。
继承后的能力
子类的共性成员都被父类提取了,那么子类要使用怎么办?
子类继承父类后,将会隐式继承父类的全部成员,但不包括构造函数。
在继承后,访问其父类成员,会受到访问修饰符的限制。故,修饰为private的私有成员不会访问到。
继承的特性
1.继承的单根性:
一个子类只能有一个父类,就比如一我的只有一个父亲。
2.继承的传递性:
例如, ClassC 派生自 ClassB,而且 ClassB 派生自 ClassA,则 ClassC 会继承在 ClassB 和 ClassA 中声明的成员。
依次顺序能够不断向上取。
图例:
继承被后的秘密 —— 子类和父类的构造函数(难点)
给父类编写了一个构造函数,示例代码以下:
1 class Animal 2 { 3 public Animal(string name,string gender,int age) 4 { 5 this.Name = name; 6 this.Gender = gender; 7 this.Age = age; 8 } 9 10 private string name; 11 public string Name 12 { 13 get { return name; } 14 set { name = value; } 15 } 16 17 private string gender; 18 public string Gender 19 { 20 get { return gender; } 21 set { gender = value; } 22 } 23 24 private int age; 25 public int Age 26 { 27 get { return age; } 28 set { age = value; } 29 } 30 31 public void Run() 32 { 33 Console.WriteLine("奔跑。。。"); 34 } 35 36 private void ri() 37 { } 38 39 } 40 41 class Cat:Animal 42 { 43 public void CatchMouse() 44 { 45 Console.WriteLine("抓老鼠。。。"); 46 } 47 } 48 49 class Dog:Animal 50 { 51 public void GuardHouse() 52 { 53 Console.WriteLine("看家护院。。。"); 54 } 55 } 56 57 class Cattle:Animal 58 { 59 public void Plowland() 60 { 61 Console.WriteLine("耕田。。。"); 62 } 63 }
尝试运行:
为何会提示报这个错误?意思说父类不能没有一个无参的构造函数。
学过构造函数的应该都会知道,类在没有指定任何构造函数的状况下,程序默认会指派一个无参的构造函数。
上述的例子因为咱们手动添加的那个构造函数,默认的构造函数就被清除掉了。
在暂且不知道缘由的状况下,咱们尝试补全那个无参的构造函数,在进行生成代码,此时编译经过没有报错。
根据此特征咱们能够推测子类和父类的构造函数必定有关系,但必定不是继承关系
尝试调用刚刚定义的父类无参构造函数,在调用列表并无显示,只显示了类自身的一个无参构造函数。
证实了子类不能继承父类的构造函数。
经过调试代码监视子类实例化对象的过程,看它到底和父类的构造函数发生了什么。
经过调试发如今建立子类对象时的代码执行逻辑以下:
子类会首先去默认执行父类的无参构造函数,而后在执行本身的构造函数
这条定论就很好的解释了,为何在上述例子为何会出现的错误。可是子类又为何要先去执行父类的构造函数?
解释:
由于子类继承了父类的成员,这一项描述只能说明子类拥有的权利,并不表明子类去执行了。
在原则上要使用类的成员,必需要经过类的实例对象去调用。因此子类要调用到父类的成员,就必须去经过调用
父类的构造函数,在子类的内部建立一个父类的对象,以便本身去调用父类的成员。
总结:
子类始终要使用父类的一个构造函数在本身内部建立一个父类对象,为了调用父类的成员。
子类默认调用父类的无参构造函数,因此在显示编写一个有参构造函数时致使父类没有了无参构造函数,从而编译出错。
在子类中使用显示调用父类构造函数
做用1:
提升代码重用性,子类无需在类中定义,直接使用父类的。
做用2:
上述例子讲过子类在实例化对象时会调用父类的默认无参构造函数,由于子类的目的就是经过父类构造函数建立一个对象。
经过这样显示的调用,那么在父类有没有无参构造函数都没什么关系了。
子类中存在和父类中相同的成员
示例:
根据VS给咱们提示的消息,咱们能够看出,当代码中存在子类的成员和父类的成员相同的时候,子类的成员将父类的成员隐藏了。
隐藏事后子类将没法访问到父类的成员。若是是刻意为之,咱们可使用new 关键字显示的说明,从而提升可读性。
指定new关键字:
此时提示的波浪线已消除。
其余注意点
在C#中,全部的类都直接或间接的继承自object类(当咱们定义一个类的时候,若是没有给该类指定继承一个类,那么这个类就继承了object类)。