细说C#继承
简介
继承(封装、多态)是面向对象编程三大特性之一,继承的思想就是摈弃代码的冗余,实现更好的重用性。
继承从字面上理解,无外乎让人想到某人继承某人的某些东西,一个给一个拿。这个语义在生活中,就像
家族继承财产,爷爷将财产继承给儿女,儿女在将财产继承给子孙,有些东西可以继承有些的东西只继承给
某人。映射到编程当中,其思想也大致如此。
通过示例引出继承的作用
在代码中定义个三个类:Cat猫、Dog狗、Cattle牛。

从类图上可以看出红色标识区域,三个类的定义出现了大量的冗余(字段、属性、方法),那么在编写代码时就会出现大量的重复代码。
试想一下,随着业务功能的扩展,可能会出现更多类,那么冗余(重复的代码)会更多。比如出现同样会造成冗余的类:
Pig猪、Panda熊猫、Sheep羊......等等。这些类同样会有相同的特征:名称、性别、年龄、奔跑(字段、属性、方法)。
如何解决此类冗余问题 —— 使用继承
继承的思想:
当我们定义了多个类,这多个类都存在重复的成员(共性)。我们可以将这些重复的成员单独的提取封装到一个类中,作为这些具有相同特征类的父类。
将此思想作用于上述的三个类
提取共性:可以直观看出重复的具有共性的项目有: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 中声明的成员。
依次顺序可以不断向上取。
图例:

继承被后的秘密 —— 子类和父类的构造函数(难点)
给父类编写了一个构造函数,示例代码如下:
class Animal
{
public Animal(string name,string gender,int age)
{
this.Name = name;
this.Gender = gender;
this.Age = age;
} 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("奔跑。。。");
} private void ri()
{ } } 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:
上述例子讲过子类在实例化对象时会调用父类的默认无参构造函数,因为子类的目的就是通过父类构造函数创建一个对象。
通过这样显示的调用,那么在父类有没有无参构造函数都没什么关系了。
子类中存在和父类中相同的成员
示例:

根据VS给我们提示的消息,我们可以看出,当代码中存在子类的成员和父类的成员相同的时候,子类的成员将父类的成员隐藏了。
隐藏过后子类将无法访问到父类的成员。如果是刻意为之,我们可以使用new 关键字显示的说明,从而提高可读性。
指定new关键字:

此时提示的波浪线已消除。
其他注意点
在C#中,所有的类都直接或间接的继承自object类(当我们定义一个类的时候,如果没有给该类指定继承一个类,那么这个类就继承了object类)。
细说C#继承的更多相关文章
- 细说 js 的7种继承方式
在这之前,先搞清楚下面这个问题: function Father(){} Father.prototype.name = 'father'; Father.prototype.children = [ ...
- 细说Java主流日志工具库
概述 在项目开发中,为了跟踪代码的运行情况,常常要使用日志来记录信息. 在Java世界,有很多的日志工具库来实现日志功能,避免了我们重复造轮子. 我们先来逐一了解一下主流日志工具. java.util ...
- .NET面试题解析(04)-类型、方法与继承
系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 做技术是清苦的.一个人,一台机器,相对无言,代码纷飞,bug无情.须梦里挑灯,冥思苦想,肝血暗耗,板凳坐穿 ...
- C#综合揭秘——细说多线程(上)
引言 本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个方面介绍多线程的开发. 其中委托的BeginInvoke方法以及回调函数最为常用. 而 I/O线程 ...
- [转]C#综合揭秘——细说进程、应用程序域与上下文之间的关系
引言 本文主要是介绍进程(Process).应用程序域(AppDomain)..NET上下文(Context)的概念与操作.虽然在一般的开发当中这三者并不常用,但熟悉三者的关系,深入了解其作用,对提高 ...
- 【重走Android之路】【Java面向对象基础(二)】细说String、StringBuffer和StringBuilder
[重走Android之路][基础篇(二)][Java面向对象基础]细说String.StringBuffer和StringBuilder 1.String String是Java中的一个final ...
- C#综合揭秘——细说多线程
一.线程的定义 1. 1 进程.应用程序域与线程的关系 进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源.进程之间是相对独立的,一个进程无法访问另一个进程 ...
- 细说Lucene源码(一):索引文件锁机制
大家都知道,在多线程或多进程的环境中,对统一资源的访问需要特别小心,特别是在写资源时,如果不加锁,将会导致很多严重的后果,Lucene的索引也是如此,lucene对索引的读写分为IndexReader ...
- 【细说Java】Java的重写与隐藏
重写与隐藏,有些书上或介绍上可能名称不一样,但都大差不差.以前只了解重写,隐藏也听说过,但没有详细了解过,趁现在,整理一下这两方面的内容吧. 首先,先说一下概念方面的东西. 重写 重写:子类继承了父类 ...
随机推荐
- 【好书分享】容器网络到kubernetes网络
Nginx 公司的 Michael Hausenblas 发布了一本关于 docker 和 kubernetes 中的容器网络的小册子.这份资料一共 72 页,是大家由浅入深的了解 Docker 和 ...
- Spring Boot + Spring Cloud 实现权限管理系统 后端篇(一):Kitty 系统介绍
在线演示 演示地址:http://139.196.87.48:9002/kitty 用户名:admin 密码:admin 温馨提示: 有在演示环境删除数据的童鞋们,如果可以的话,麻烦动动小指,右键头像 ...
- springboot-3-其他配置
1, 热部署: 有jrebel的话, 不用了, 不如jre好用 原理: 使用两个classLoad, 一个加载不改变的jar, 另一个加载可更改的jar, 发生改变后, 舍弃可更改的jar重新rest ...
- C++中模板与泛型编程
目录 定义一个通用模板 模板特化和偏特化 模板实例化与匹配 可变参数模板 泛型编程是指独立与任何类型的方式编写代码.泛型编程和面向对象编程,都依赖与某种形式的多态.面向对象编程的多态性在运行时应用于存 ...
- spring中获取applicationContext
常用的5种获取spring 中bean的方式总结: 方法一:在初始化时保存ApplicationContext对象代码:ApplicationContext ac = new FileSystemXm ...
- Low Power之CPF/UPF
1 CPF The Common Power Format is a standard promoted by the Low Power Coalition at Si2. CPF is also ...
- Java 和 JSP 实现网站访问量统计 (刷新过滤)
java 和 JSP 实现的统计网站访问量,不需要数据库,将数据存储在 指定位置的 txt 文件中,代码块分为两部分 首先, java 部分: import java.io.File; import ...
- Linux man C++ 库函数
默认情况下,linux是的man是不能查阅C++的标准库函数的,这个很不方便,那有没有办法可以直接man C++标准库函数呢? 当然有,不过要自己动手,自己动手,才能丰衣足食! 1. 下载安装manp ...
- Node.js学习笔记(七) --- Node.js的静态文件托管、路 由、EJS 模板引擎、GET 、POST
1 . Nodejs 静态文件托管静态 web 服务器封装 2 . 路由 官方解释: 路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET.POST 等)组成的, ...
- 7行代码看EntityFramework是如何运行
这段时间在项目中运用Entity Framework作为底层数据交互框架.一个字,爽.不仅提高了开发效率,省了很多代码,而且数据库也规范了很多.按照网上的一些教程初步学习,然后实际运用了,再结合MVC ...