C#面向对象核心之二:继承

面向对象的三大特性之一的封装,解决了将对同一对象所能操作的所有信息放在一起,实现统一对外调用,实现了同一对象的复用,降低了耦合。

但在实际应用中,有好多对象具有相同或者相似的属性,比如有一个对象 果树(FruitTree),它有成员属性叶子(Leaf),会开花(Flower),有树干(Stem),有树根(Root),它还会生长(Growth方法)。

有另一个对象苹果树(AppleTree)它也是果树,具有果树所有特性,那么我们在编程的时候,定义了一个苹果树对象,假如再有一个桔子树(OrangeTree)、桃树(PeachTree)呢,我们不能一直复制这个对象改名字实现吧?这里就要用到面向对象的第二个特性:继承。

1.什么是继承

上面的果树(FruitTree) 和桔树(OrangeTree)之间是一个“ is-a ”的关系,即我们可以说 “桔树是果树”。在面向对象编程中,我们把这种关系称为 “继承”,即桔树继承自果树。或者说,桔树类是果树类的派生类;也可以说果树是父类,桔树是子类。同样的苹果树也可以继承果树,那么苹果树也可以说是果树的子类。在这里我们发现一个类可以有多个派生类,也就是一个类可以被多个类继承.

继承相关的概念:

(1) 当一个类A能够获取另一个类B中所有非私有的数据和操作的定义作为自己的部分或全部成分时,就称这两个类之间具有继承关系。

(2) 被继承的类B称为父类或基类,继承了父类的类A称为子类或派生类.

2.继承的特点

上面的例子,假如苹果树继承自果树,那么苹果树除了具有果树所有的属性(叶子,根、花)和方法(生长)之外,苹果树还有自己特有的一些属性,比如有自己的果实苹果(Apple); 同样桃树有自己的果实桃子(Peach),因此继承的子类可以有自己独有的成员(属性或方法等)。

特点一:派生类除了继承父类的特性外,还可以有自己独有特性

上面说到的父类果树(FruitTree)除了有叶子、根、花这些公有的成员之外,也可以有自己的私有成员,比如种类(落叶果树、常绿果树),而“种类”这个成员,并不是它的子类苹果树(AppleTree)和桔树(OrangeTree)所具有的,因此是私有成员,子类继承父类后,并不能拥有父类的私有成员。

特点二:子类不能拥有父类的私有成员

还是上面的例子,假如果树有一个公有方法生长(Growth),它有两个子类桃树和苹果树,那么子类也同时拥有生长这个方法,但是桃树和苹果树的生长过程是不同的,我们可以修改这个方法以适应不同种类果树的生长。

特点三:子类可以以自己的方式实现父类的功能(即方法重写)

3.继承的实现

通过上面的例子,我们已经对继承很熟悉了,抛开概念。简单的说,继承一词本就来源于生活,有财产继承,精神继承。面向对象编程只不过就是把这些概念抽象化而已,通俗来说就是“苹果树是一颗果树”

代码实现上面的例子

  1 /// <summary>
  2     /// 果树类
  3     /// </summary>
  4     class FruitTree
  5     {
  6         /// <summary>
  7         /// 名称
  8         /// 说明:修饰符 protected 保护访问。只限于本类和子类访问,实例不能访问。
  9         /// </summary>
 10         protected string name;
 11         /// <summary>
 12         /// 构造函数
 13         /// </summary>
 14         public FruitTree()
 15         {
 16             this.name = "无名";
 17         }
 18         /// <summary>
 19         /// 构造函数二
 20         /// </summary>
 21         /// <param name="name"></param>
 22         public FruitTree(string name)
 23         {
 24             this.name = name;
 25         }
 26         object _leaf;
 27         object _root;
 28         object _flower;
 29         string _type;
 30         /// <summary>
 31         /// 叶子(公有属性)
 32         /// </summary>
 33         public object leaf
 34         {
 35             get { return _leaf; }
 36             set { _leaf = value; }
 37         }
 38         /// <summary>
 39         /// 根(公有属性)
 40         /// </summary>
 41         public object root
 42         {
 43             get { return _root; }
 44             set { _root = value; }
 45         }
 46         /// <summary>
 47         /// 花(公有属性)
 48         /// </summary>
 49         public object flower
 50         {
 51             get { return _flower; }
 52             set { _flower = value; }
 53         }
 54         /// <summary>
 55         /// 类别(不定义修饰符,默认为私有)
 56         /// </summary>
 57         string type
 58         {
 59             get { return _type; }
 60             set { _type = value; }
 61         }
 62
 63     }
 64
 65     /// <summary>
 66     /// 苹果树类
 67     /// 继承自:果树类
 68     /// </summary>
 69     class AppleTree:FruitTree
 70     {
 71         string _myName;
 72         /// <summary>
 73         /// 构造函数
 74         /// 说明:子类调用父类同样的构造函数,需要使用 :base()
 75         /// </summary>
 76         public AppleTree():base()
 77         {
 78         }
 79         /// <summary>
 80         /// 构造函数二
 81         /// 说明:子类调用父类同样的构造函数,需要使用 :base(name)
 82         /// </summary>
 83         /// <param name="name"></param>
 84         public AppleTree(string name):base(name)
 85         {
 86             _myName = name;
 87         }
 88
 89         /// <summary>
 90         /// 返回果实的名字
 91         /// </summary>
 92         /// <returns></returns>
 93         public string MyFruitName()
 94         {
 95             return "我是:" + _myName + ";我的果实叫:苹果";
 96         }
 97     }
 98     /// <summary>
 99     /// 桔树类
100     /// 继承自:果树类
101     /// </summary>
102     class OrangeTree : FruitTree
103     {
104         string _myName;
105         /// <summary>
106         /// 构造函数
107         /// 说明:子类调用父类同样的构造函数,需要使用 :base()
108         /// </summary>
109         public OrangeTree(): base()
110         {
111         }
112         /// <summary>
113         /// 构造函数二
114         /// 说明:子类调用父类同样的构造函数,需要使用 :base(name)
115         /// </summary>
116         /// <param name="name"></param>
117         public OrangeTree(string name): base(name)
118         {
119             _myName = name;
120         }
121
122         /// <summary>
123         /// 返回果实的名字
124         /// </summary>
125         /// <returns></returns>
126         public string MyFruitName()
127         {
128             return "我是:"+_myName+";我的果实叫:桔子";
129         }
130     }

调用子类:

//调用子类
AppleTree appleTree = new AppleTree("苹果树");
string myName = appleTree.MyFruitName();
//返回结果为:我是:苹果树;我的果实叫:苹果
//调用子类
OrangeTree orangeTree = new OrangeTree("桔子树");
string myName = orangeTree. MyFruitName ();
//返回结果为:我是:桔子树;我的果实叫:桔子

通这段代码,我们可以看到有了基类果树,那么我们再有几百种树,只需要一个继承就可以了,对于子类AppleTree.MyFruitName()返回名字这个方法,在不同子类中可以特有,就是继承的特点,可以增加特有成员。虽然对于独有特点需要在每个子类中单独定义,但是共享父类成员已经让我们省去不少工作量了,最重要的程序的结构更加清晰、易于维护了。

4.继承的缺点

看到这个标题,小伙伴们也许很惊讶,既然说了这么多面向对象继承特性的好处,原来还有缺点。当然,世界上没有完美的东西,继承也是。

 缺点一:父类变化,子类不得不变;

   缺点二:继承破坏了包装,父类的细节暴露给了子类。

前一节说了封装的独立特性,是减少了耦合性,而继承其为了实现复用却增加了耦合性。

说到这里小伙伴们纠结了,那么到底要不要使用继承,答案是肯定的,它的优点和光芒掩盖了缺点,也就是说好处更多一些。这里说明它的缺点,就是提醒我们在使用过程中尽量避免它的缺点所带来的后果。

那么要如何才能很好的使用继承呢?我们应该注意这么几点:

a.当两个对象间是“is a”关系时,可以使用继承(比如苹果树是树);b.当两个对象是“has a”关系时,不宜使用继承(比如手是人的一部分,不能让手继承人);

对于继承的优缺点,我们记住一点:要合理使用继承,才能发挥最佳效果,而不是盲目使用。

作为面向对象的三大特性之一:继承,可以说是学好面向对象编程的重中之重。

要点:

1:父类中的私有成员,派生类是绝不能访问;

2:C#要求一个类只能有一个直接基类;

3:被“sealed”关键字修饰的类将不能被继承;

4:被“protected”修饰的成员或者数据可以直接被派生类访问,属于“可以在家族里分享的秘密”。

5:善用“base”关键字,显示调用合适的自定义基类构造函数而不是使用默认构造函数。

6:继承需要合理使用才能发挥最佳效果,一般情况下适用于“is a”关系,不适用“has a”关系。

时间: 2024-08-07 14:03:51

C#面向对象核心之二:继承的相关文章

Javascript面向对象编程(二):构造函数的继承

这个系列的第一部分,主要介绍了如何"封装"数据和方法,以及如何从原型对象生成实例. 今天要介绍的是,对象之间的"继承"的五种方法. 比如,现在有一个"动物"对象的构造函数. function Animal(){ this.species = "动物"; } 还有一个"猫"对象的构造函数. function Cat(name,color){ this.name = name; this.color = col

Javascript面向对象编程(二):构造函数的继承 作者:yuan一峰

Javascript面向对象编程(二):构造函数的继承 作者: 阮一峰 日期: 2010年5月23日 这个系列的第一部分,主要介绍了如何"封装"数据和方法,以及如何从原型对象生成实例. 今天要介绍的是,对象之间的"继承"的五种方法. 比如,现在有一个"动物"对象的构造函数. function Animal(){ this.species = "动物"; } 还有一个"猫"对象的构造函数. function

面向对象(一)—继承与多态

又一次深入的学习设计模式,发现了很多以前感觉不是问题的问题,这才发现原来自己不是真的理解了.通过这次的深入学习,才开始慢慢感受到OO的魅力所在. 从C#学习到设计模式,再到机房收费系统个人版和合作版,再到我们做的项目,我们真正的朝着面向对象编程了吗?我的项目中,先不说泛型.委托.集合的利用率,就是基本的继承.多态用的少之又少. 下面将为大家解说"OO引领编程"之--继承和多态 继承篇 一.简介 俗话说:龙生龙凤生凤,老鼠的儿子会打洞.可以理解为,继承就是小辈拥有父辈流传下来的东西. 在

类与对象 面向对象和面向过程对比 面向对象三大特征:封装 继承 多态

 初识面向对象 面向过程: 一切以事务的发展流程为中心. 面向对象: 一切以对象为中心. 一切皆为对象. 具体的某一个事务就是对象 打比方: 大象进冰箱 步骤: 第一步, 开门, 第二步, 装大象, 第三步, 关门 面向对象:大象, 你进冰箱. 此时主语是大象. 我操纵的是大象. 此时的大象就是对象 1. 面向过程: 一切以事物的流程为核心. 核心是"过程"二字, 过程是指解决问题的步骤, 即, 先?干什么, 后干什么. 基于该思想编写程序就好比在编写一套流水线. 是一种机械 式的编程

C#编程语言与面向对象——核心

面向对象的核心 (1).封装 封装的类=数据+对此数据所进行的操作(即算法) 封装起外界不必需要知道的东西,指向外界展现可供展示的东西. 小到一个简单的数据结构,大到一个完整的软件子系统.静态的如某软件系统要收集数据信息项,动态的如某个工作处理流程,都可以封装到一个类中. 具备这种意识,是掌握面向对象分析与设计技巧的关键. (2).抽象 在使用面向对象的方法设计一个软件系统时,首先就要区分出现实世界中的事务所属的类型,分析它们拥有哪些性质与功能,再将他们抽象为在计算机虚拟世界中才有意义的实体——

【Java面向对象基础(二)】细说String、StringBuffer和StringBuilder

[喵"的Android之路][基础篇(二)][Java面向对象基础]细说String.StringBuffer和StringBuilder 1.String String是Java中的一个final类,主要用于字符串的处理. 1.1 不可变性 String内的字符串是不可变的,每一次修改都会重新生成一个新的String对象实例. 例: 1 // 在堆中会创建一个"Hello"字符串实例,把地址赋给对象a 2 String a = new String("Hello&

python的面向对象的特性(继承、封装、多态)

创建自已对象就python非常核心的概念,事实上,python被称为面向对象语言,本章会介绍如何创建对象.以及面向对象的概念:继承.封装.多态. 多态: 可对不同类的对象使用同样的操作. 封装:对外部世界隐藏对象的工作细节. 继承:以普通的类为基础建立专门的类对象. 多态 面向对象程序设计最有趣的特性是多太,它是是让大多数人犯晕的特性.所以,先来介绍这个. 多态意思是"有多种形式".多态意味着就算不知道变量所引用的对象类是什么,还是能对它进行操作,而它也会根据对象(或类)类型的不同而表

JAVA学习--面向对象的特征二:继承性

* 一.面向对象的特征二:继承性 * 1.为什么要设计继承性?  *  * 2.通过"class A extends B"类实现类的继承.  *   子类:A  父类(或基类 SuperClass):B  *     * 3.子类继承父类以后,父类中声明的属性.方法,子类就可以获取到.  *    明确:当父类中有私有的属性或方法时,子类同样可以获取得到,只是由于封装性的设计,使得子类不可以直接  *        调用罢了.  *   子类除了通过继承,获取父类的结构之外,还可以定义

C++ Primer 学习笔记_67_面向对象编程 --转换与继承、复制控制与继承

面向对象编程 --转换与继承.复制控制与继承 I.转换与继承 引言: 由于每一个派生类对象都包括一个基类部分,因此能够像使用基类对象一样在派生类对象上执行操作. 对于指针/引用,能够将派生类对象的指针/引用转换为基类子对象的指针/引用. 基类类型对象既能够作为独立对象存在,也能够作为派生类对象的一部分而存在,因此,一个基类对象可能是也可能不是一个派生类对象的部分,因此,没有从基类引用(或基类指针)到派生类引用(或派生类指针)的(自己主动)转换. 关于对象类型,尽管一般能够使用派生类型的对象对基类