【1】4.2 继承的类型

1.C#不支持类的多继承,但它支持一个接口继承自多个接口。

2.单继承:单继承允许一个类继承自另外一个基类,C#支持。

3.多级继承:多级继承允许创建一个类继承自它的父类,而父类又继承自它的爷爷类,多级继承会是一个庞大的谱系结构。C#支持这一点。

4.接口继承:一个接口继承自另外一个接口,这里也允许一个接口继承自多个接口。

【2】4.2.2 结构和类

1.struct本身不支持继承,但它们可以支持接口的继承。

2.struct和class之间的区别:

  • struct总是派生自System.ValueType类,它可以实现任意数量的接口。
  • class则总是派生自System.Object类或者另外的某个类。它们也可以实现任意数量的接口。

【3】4.3 实现继承

1.继承于Object类的声明可以省略不写。

2.如果同时有父类和接口,类必须是第一个,写在所有接口之前。

3.如果一个类(或者一个结构)实现了接口,那么它继承的类和接口之间使用,进行分隔。

【4】4.3.1 虚方法

1.virtual修饰的方法为虚方法,暗示其子类最好有自己的实现。
2.override修饰的方法为重写方法,表示覆盖了基类原有方法的实现。

3.lambda操作符。

4.ToString方法作为Object类的成员。

5.当重写基类的方法时,方法签名(所有的参数类型以及方法名)和返回值必须完全匹配,如若不然,那么你创建的是一个新方法,而不是对基类方法的重写。

6.

1、当调用一个对象的函数时,系统会直接去检查这个对象申明定义的类,即申明类,看所调用的函数是否为虚函数;

2、如果不是虚函数,那么它就直接执行该函数。而如果有virtual关键字,也就是一个虚函数,那么这个时候它就不会立刻执行该函数了,而是转去检查对象的实例类。

3、在这个实例类里,他会检查这个实例类的定义中是否有重新实现该虚函数(通过override关键字),如果是有,那么OK,它就不会再找了,而马上执行该实例类中的这个重新实现的函数。而如果没有的话,系统就会不停地往上找实例类的父类,并对父类重复刚才在实例类里的检查,直到找到第一个重写了该虚函数的父类为止,然后执行该父类里重写后的函数。

7.virtual修饰的方法。其子类除了override,还可用new来修饰。

【5】4.3.2 多态性

假如Shape的Draw方法不是virtual修饰的虚方法或者Rectangle没有重写Draw方法,那么将Rectangle变量当做参数传递给DrawShape,执行的最终会是Shape.Draw方法,那么输出就会以"Shape With"开头。

如果有重写方法,那么按照重写的这个来。如果没有,则按照的你所定的方法来执行。

【6】4.3.3 隐藏方法

1.假如基类和子类都拥有一个相同签名的方法,并且它们没有分别用virtual和override进行声明, 那么子类版本将会隐藏基类版本的方法。

2.基类与派生类方法名相同,方法的实现不同。当派生类抛出一个编译警告时,可以用new来隐藏基类的成员。

3.如果不用new关键字,也可重命名方法名,或重写基类方法(假如存在跟你目的一致的虚方法的话)。而,万一你的方法有其他的外部引用的话,简单的修改方法名会导致代码出错。

4.new关键字不应该用来刻意隐藏基类的成员。它的主要目的只是为了解决版本冲突,当基类定义了某些派生类已经处理过的内容的时候。

【7】4.3.4 调用方法的基类版本

1.C#提供了一个特殊的语法以便子类可以调用父类的方法:base.<MethodName>。

base.Move(newPosition);

2.通过base关键字,你可以调用父类任意方法,并不单单只是你重写的那个方法。

【8】4.3.5 抽象类和抽象方法

1.C#允许将类和方法声明成abstract。一个抽象类无法被实例化,而一个抽象方法不能拥有任何方法体并且继承抽象类的非抽象子类必须实现此抽象方法。显而易见,抽象方法自动就是virtual方法(并且你不能再给虚方法加一个virtual修饰符,否则会得到一个编译错误)。任何含有抽象方法的类必须是一个抽象类。

2.抽象类无法被实例化,抽象类无方法体,无实现方法,都由子类去实现抽象类里所有抽象成员。

3.(子类)在方法体里直接抛出一个NotImplementationException异常来作为一种方法实现,但这往往只是开发过程中的一种临时手段而已,在该方法被正常调用的时候你仍然需要实现它。

【9】4.3.6 密封类和密封方法

1.假如你不想让你的类能被其他类继承,你可以将它声明成sealed,这样它就不能拥有任何子类了。而如果你将某个方法声明成sealed,意味着它不允许被重写。

2.使用密封类的理由:

  • 如果你需要将某个类或者方法标记为sealed,最可能的状况就是这个类或者方法,是用于某个类库、类又或者别的还在编辑的类,用于实现一些内部操作,任何对于这个类的重写都可能会导致代码的不稳定。举个例子,假如你从未测试过继承又或者还没决定继承的设计策略的话,将你的类声明成sealed不失为一个好方法。
  • 通过sealed类,编译器知道它肯定不会有派生类,因此不需要为它创建虚方法表,这点能提高性能。string类就是密封类。因为我从没见过任何一个成型的应用程序不使用string类的,因此这个类最好拥有更高的性能。将类声明成sealed对编译器来说是一个很好的暗示(good hint)。
  • 将方法声明成sealed的理由跟class差不多。某个方法可能是重写了基类的虚方法,假如将它声明成sealed方法,编译器就知道后续的类不可能再扩展此方法的虚方法表(vtable)了,就到此为止。

3.为了给方法或属性使用sealed修饰符,它们必须首先是override基类的virtual方法或者属性。假如你不想基类的方法或属性被重写,就别把它们声明成virtual方法或属性。

【10】4.3.7 派生类的构造函数

1.构造函数总是按照类的继承顺序依次调用的。System.Object的构造函数总是第一个,然后挨个调用,直到编译器遇到要实例化的那个类为止。为了创建Ellipse的实例,编译器先调用了Object构造函数,然后是Shape构造函数,最后才是Ellipse的构造函数。每个构造函数只处理它们自己字段的初始化。

2.base  命名参数。

【11】4.4.1 访问修饰符

1.public,protected,private是逻辑访问修饰符,而internal则是物理访问修饰符,它是按程序集来界定的。

2.你不能将包含在命名空间下的顶级类用protected,private,或者protected internal进行修饰,因为这些访问等级对它们毫无意义。因此,这些修饰符只能用在成员上。然而,你可以将这些修饰符定义在嵌套类型(就是一个类型里面再套一个其他类型)上,因为在这种情况下,内在的嵌套类型也可以当成一个成员来看,因此像下面这么写是正确的:

public class OuterClass
{
protected class InnerClass
{
// ...
}
// ...
}

3.假如你定义了一个嵌套类型,内在类型永远能访问外在类型的所有成员。因此,在上面的例子里,InnerClass里的所有代码都可以访问OuterClass里的全体成员,即使那些成员是private声明的也不例外。

【12】4.4.2 其他修饰符

new,static,virtual,abstract,override,sealed,extern。

【13】4.5 接口

1.接口不允许提供任何成员的任何实现。总的来说,接口只能包含方法,属性,索引和事件的声明。

2.接口里的所有成员肯定是abstract的,接口不能有任何具体实现代码,它是纯粹的抽象。

3.接口既没有构造函数(不能实例化的对象有构造函数也没用啊),也没有字段(因为意味着需要一些内部实现)。接口也不许包含操作符重载。

4.接口内的成员不允许声明任何修饰符,接口成员总是隐式地声明为public,而且它们不能被virtual修饰。如何实现取决于继承接口的类。因此,由实现类来决定访问修饰符是最好的。

【14】4.5.1 定义和实现接口

1.接口名I。

2.实现接口和继承自某个父类是完全独立的,互不影响。

3.接口引用完全可以当成类引用来用,接口引用可以指向任何实现了这个接口的类。

【15】4.5.2 派生的接口

1.在方法的实现和调用过程中,你不需要知道你实际操作的对象具体是哪个类——你只需要知道那个对象只要实现了IBankAccount接口就行。

2.相当于一个接口继承另一个接口,这个接口比继承的接口多了一种办法。然后在子类中实现这个办法。

【16】4.6 is 和as 运算符

1.假如你有一个方法,接收一个Object类型的参数,但是你在方法体内想访问IBankAccount对象的成员时候,怎么办?

这个时候你就需要使用强制转换,将Object对象强制转换成IBankAccount类型,就像下面这样:

public void WorkWithManyDifferentObjects(object o)
{
IBankAccount account = (IBankAccount)o;
// work with the account
}

传递过来的对象没有实现IBankAccount接口,这个时候你想进行强制转换,程序就会报错。

2.as运算符和强制转换操作符很类似,都是转换对象,返回一个引用。但是在转换失败时,as运算符不会抛出一个InvalidCastException异常,而是返回一个空引用,如下所示:

public void WorkWithManyDifferentObjects(object o)
{
IBankAccount account = o as IBankAccount;
if (account != null)
{
// work with the account
}
}

这里在使用as运算符之前最好先判断一下参数o是否为null,如果对null值进行as转换会直接抛出一个NullReferenceException。

3.is运算符会根据某个对象是否是指定类型返回true和false。假如表达式返回的是true值,则将该对象转换成指定类型并赋值给随后的变量,就像下面这样:

public void WorkWithManyDifferentObjects(object o)
{
if (o is IBankAccount account)
{
// work with the account
}
}

is运算符最后追加变量声明是C# 7.0开始的新特性,这个特性属于模式匹配的一部分。

【17】

本章主要介绍了C#里面代码是如何进行继承的,详细讲解了C#是如何提供接口的多重继承和类的单继承,并且解释了C#是如何提供更丰富的语法设计使得构造继承代码更加的健壮易用。

这其中包括override关键字,它用来声明一个方法重写了基类的同名方法;也包括new关键字,它定义了子类隐藏了父类的同名方法;还包括严格的构造函数初始化规则,来确保构造函数之间以更稳健的方式进行交互。

参考网址:https://www.cnblogs.com/zenronphy/p/ProfessionalCSharp7Chapter4.html#41-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1

C#高级编程第11版 - 第四章 索引的更多相关文章

  1. C#高级编程第11版 - 第六章 索引

    [1]6.2 运算符 1.&符在C#里是逻辑与运算.管道符号|在C#里则是逻辑或运算.%运算符用来返回除法运算的余数,因此当x=7时,x%5的值将是2. [2]6.2.1 运算符的简写 1.下 ...

  2. C#高级编程第11版 - 第五章 索引

    [1]5.1 泛型概述 1.通过泛型,你可以创建独立于特定类型(contained types)以外的方法和类,而不用为不同类型编写多份同样功能的代码,你只需要创建一个方法或者类. 2.泛型类使用泛型 ...

  3. C#高级编程第11版 - 第三章 索引

    [1]3.1 创建及使用类 1.构造函数:构造函数的名字与类名相同: 使用 new 表达式创建类的对象或者结构(例如int)时,会调用其构造函数.并且通常初始化新对象的数据成员. 除非类是静态的,否则 ...

  4. C#高级编程第11版 - 第七章 索引

    [1]7.1 相同类型的多个对象 1.假如你需要处理同一类型的多个对象,你可以使用集合或者数组. 2.如果你想使用不同类型的不同对象,你最好将它们组合成class.struct或者元组. [2]7.2 ...

  5. 【转】apue《UNIX环境高级编程第三版》第一章答案详解

    原文网址:http://blog.csdn.net/hubbybob1/article/details/40859835 大家好,从这周开始学习apue<UNIX环境高级编程第三版>,在此 ...

  6. 2019-1-17 前言 C#高级编程(第11版)

    C#已更新为更快的速度.主要版本7.0是2017年3月发布,次要版本7.1和7.2很快发布在2017年8月和2017年12月.通过项目设置,您可以与每个应用程序一起分发,是开源的,不可用仅适用于Win ...

  7. C#高级编程第11版 - 第八章 索引

    [1]8.1 引用方法 1.委托是指向方法的.NET地址变量. 2.委托是类型安全的类,定义了返回类型和参数类型.委托类不单单只包含一个方法引用,它也可以保存多个方法的引用. 3.Lambda表达式直 ...

  8. C#高级编程第11版 - 第九章 索引

    [1]9.1 System.String 类 String类中关键的方法.如替换,比较等. [2]9.1.1 构建字符串 1.String类依然有一个缺点:因为它是不可变的数据类型,这意味当你初始化一 ...

  9. C#高级编程第11版 - 第二章 索引

    [1]2.1.1 Hello,World! 1. using static System.Console; // ... WriteLine("Hello World!"); 提前 ...

随机推荐

  1. CentOS8 部署SqlServer

    官方文档https://docs.microsoft.com/zh-cn/sql/linux/quickstart-install-connect-red-hat?view=sql-server-li ...

  2. 从零实现Linux一键自动化部署.netCore+Vue+Nginx项目到Docker中

    环境搭建 1.安装Linux,这里我用的阿里云服务器,CentOS7版本 2.进入Linux,安装Docker,执行以下命令 sudo yum update #更新一下yum包 sudo yum in ...

  3. C#访问Access数据库提示未安装ISAM

    解决办法 1.在前面加上Jet OLEDB:,如: Jet OLEDB:Database Password='zt' <add name="ConStrOleDb" conn ...

  4. 【命令】ln命令

    这是linux中一个非常重要命令,请大家一定要熟悉.它的功能是为某一个文件或目录在另外一个位置建立一个同步的链接,默认是链接是硬链接,常用参数是 "-s"  . 对于ln命令,这里 ...

  5. Spring Boot使用Maven自定义打包方式

    前言:本文将告诉你如何将程序Jar与与依赖Jar及配置文件分离打包,以下列举了两种不同Maven打包方式,其打包效果一致! 一.第一种Maven打包方式,将jar及resources下全部配置文件,拷 ...

  6. Spring Boot 使用常见问题

    Json格式化时间,时区设置 spring.jackson.time-zone=GMT+8 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss json数据无 ...

  7. Maven仓库是什么

    Maven仓库是基于简单文件系统存储的,集中化管理Java API资源(构件)的一个服务.仓库中的任何一个构件都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径.得益于 Maven 的坐标 ...

  8. vue中的插值操作

    mustache语法 1.将data的文本数据插入至html语句中,使用mustache语法. v-once指令 2.在某些情况下,我们不希望界面随意的跟随改变,这个时候我们可以使用v-once的指令 ...

  9. 计算-服务器最大并发量-http协议请求-以webSphere服务器为例-考虑线程池

    请求的处理流程 广域网上有大量的并发用户同时访问Web服务器,Web服务器传递请求给应用服务器(Web容器),Web容器传递请求给EJB容器,然后EJB容器发送数据库连接请求给数据库. 请求的处理流程 ...

  10. 远程分支删除后,git branch -a还能看到的解决方法

    详情https://www.cnblogs.com/wangiqngpei557/p/6058115.html 大家在删除远程分支后 git branch -a 还是可以看到已删除的远程分支,时间一长 ...