-> 类和继承
-> 类继承
1、可以使用一个已经存在的类作为新类的基础。已存在的类称为基类,新类称为派生类
-> 派生类成员;
1、自己声明中的成员
2、基类的成员
2、要声明一个派生类,需要在类名后加入基类规格说明。基类规格说明由冒号和后面跟着用作基类的类的名称组成。派生类被描述为直接继承自列出的基类。
3、派生类被描述为扩展它的基类,因为它包含了基类的成员,加上在它自己的声明中的任何附加功能
4、派生类不能删除他所继承的任何成员
-> 访问继承的成员
继承的成员可以被访问,就像它们是派生类自己声明的一样
所有类都派生自object类
1、一个类声明的基类规格说明中只能有一个单独的类。这称为单继承
2、虽然类只能直接继承一个基类,但继承的层次没有限制。也就是说,作为基类的类可以派生自另外一个类,而它又派生自另外一个类,一直下去,直至最终到object
-> 隐藏基类的成员
-> 要隐藏一个继承的数据成员,须要声明一个新的相同类型的成员,并使用相同的名称
-> 通过派生类中声明新的带有相同签名的函数成员,可以隐藏或掩盖继承的函数成员。
请记住,签名由名称和参数列表组成,但不包括返回类型
-> 要让编译器知道你在故意隐藏继承的成员,使用new修饰符。
-> 也可以隐藏静态成员
-> 基类访问
如果派生类必须完全地访问本隐藏的继承成员,可以使用基类访问表达式访问隐藏的继承成员。基类访问表达式又关键字base后面跟着一个点和成员的名称组成。
-> 使用基类的引用
派生类的实例由基类的实例加上派生类附加的成员组成。派生类的引用指向整个对象,包括基类部分
如果有一个派生类对象的引用,就可以获取该对象基类部分的引用,使用类型转换运算符把该引用转换为基类类型
-> 虚方法和覆写方法
使用基类引用访问派生类对象时,得到的是基类的成员。虚方法可以是基类的引用访问升至派生类内。
可以使用基类引用调用派生类的方法:
1、派生类的方法和基类的方法有相同的签名和返回类型
2、基类的方法使用virtual标注
3、派生类的方法使用override标注
-> virtual和override修饰符的重要信息:
1、覆写和被覆写的方法必须有相同的可访问性。
2、不能覆写static方法或非虚方法
3、方法、属性和索引,以及事件,它们都可以被声明为virtual和override
-> 覆写标记为override的方法
覆写方法可以在继承的任意成次出现
1、当使用对象基类部分的引用调用一个覆写的方法时,方法的调用被沿派生层次上溯执行,一直到标记为override的方法的最派生版本
2、如果在更高层的派生级别有改方法的其他成员,但没有被标记为override,那么它们不会被调用。
例如:
class MyBaseClass //基类
包含一个virtual 同名方法
class MyDerivedClass:MyBaseClass //派生类
包含一个override 同名方法
class SecondDerived:MyDerivedClass //最派生类
情况一:
包含一个override同名方法:
主函数中:
SecondDerived derived = new SecondDerived();
MyBaseClass mybc = (SecondDerived)derived;
derived.Print();
mgbc.Print();
结果:都调用SecondDerived中的Print()方法
情况二:
包含一个new 同名方法:
主函数同上:
结果:先调用SecondDerived中的Print()方法
再调用MyDerivedClass中的Print()方法
-> 构造函数的执行
-> 要创建对象的基类部分,基类的一个构造函数被作为创建实例过程的一部分被调用
-> 继承层次链中的每个类在执行它自己的构造函数体之前先执行它的基类的构造函数
-> 构造函数执行顺序:
1、初始化实例成员
2、调用基类构造函数
3、执行实例构造函数的方法体
警告:在构造函数中调用虚方法是既不推荐的。在基类的构造函数被执行时,基类的虚方法会调用派生类的覆写方法,但这是在派生类的构造函数方法体被执行之前。因此,调用会在派生类没有完全初始化之前传递到派生类
-> 构造函数初始化语句
有两种形式的构造函数初始化语句
1、使用关键字base并指明使用哪一个基类构造函数
2、使用关键字this并指明应该使用当前类的哪一个另外的构造函数
基类构造函数初始化放在冒号后面,冒号紧跟着类的构造函数声明的参数列表。构造函数初始化语句由关键字base和要调用的街垒构造函数的参数列表组成
当声明一个不带构造函数初始化语句的构造函数时,它是带有base()组成的构造函数初始化语句的形式的缩写
class MyDerived:MyBase{MyDerive(){}}
class MyDerived:MyBase{MyDerive():base(){}}
另一种形式的构造函数初始化语句可以让构造过程使用当前类中其他的构造函数
public MyClass(int x):this(x,"Using Default String"){...}
这种语法很有用的另一种情况是,一个类有好几个构造函数,并且它们都需要在对象够早的过程开始执行一些公共的代码。好处:进啊少了代码的重复。这也是推荐的做法。
不好的做法:声明一个方法来进行这些公共的初始化,让所有构造函数调用这个方法。
原因:首先编译器知道方法是构造函数后会进行一些优化。
其次有的时候一些事情必须在构造函数中进行,例如readonly字段只可以在构造函数中初始化。如果尝试在其他方法中初始化,会得到一个编译错误。
回到公共构造函数,如果这个构造函数可以初始化类中所有需要初始化的东西,并且可以独立用作一个有效的构造函数,那么可以把它设置为public的构造函数。
但是如果它不能完全初始化一个对象,必须禁止类的外部调用构造函数,因为那样的话它只会初始化对象的一部分。要避免这个问题,你可以把构造函数声明为private。在可以被类的外部调用的构造函数中添加:this()
-> 类访问修饰符
术语可见的有事用作术语可访问的。他们可以互换使用。类的可访问性有两个级别:public和internal
1、标记为public的类可以被系统内任何程序集中的代码访问。
2、标记为internal的类只能被它自己所在的程序集内的类看到
-> 这是默认的可访问级别,所以,除非在类的声明中显式地指定修饰符public,程序集外部的代码不能访问该类
-> 可以使用internal访问修饰符显式地声明一个类为内部的
-> 程序集间的继承
1、基类必须被声明为public,这样他才能被从它所在的程序集外部访问
2、必须在VS工程中包括对包含该积累的程序集的引用。
为了使引用其他程序集中的类和类型更容易,不使用它们的完全限定名称,在源文件的顶部放置一个using指令,并带上将要访问的类或类型所在的命名空间。
说明:增加对其他程序集的引用和增加using指令是两回事。增加对其他程序集的引用是告诉编译器所需的类型在哪里被定义。增加using指令允许你引用其他的类而不必使用它们的完全限定名称。
-> 成员访问修饰符
-> 所有显式声明在类的声明中的成员都是互相可见的,无论它们的访问性说明如何。
-> 5个成员访问级别:
共有的、私有的、受保护的、内部的、受保护内部的
-> 必须对每个成员指定访问级别。如果不指定某个成员的访问级别,它的隐式访问级别为private
-> 成员不能比它的类更可访问。也就是说,如果一个类的可访问性限于它所在的程序集,那么类的成员个体也不能从程序集的外部看到,无论它们的访问修饰符是什么,即使是public
-> 访问成员的区域
-> 访问类别基于两个关于正在声明的类的特征
1、类是否派生自正在声明的类
2、类是否和正在声明的类在同一程序集
-> 共有成员的可访问性
public访问级别是限制性最少的,所有的类,包括程序集内部的类和外部的类都可以自由地访问成员。
对应上图中的右下
-> 私有成员的可访问性
私有访问成员级别是限制最严格的
1、private类成员只能被它自己的类的成员访问。它不能被其他的类访问,包括继承他的类
2、然而,private成员能被嵌套在它的类中的成员访问
对应上图右下的member
-> 受保护成员的可访问性
protected访问级别如同private访问级别,除了一点,它允许派生自该类的访问该成员。
对应上图的右边
-> 内部成员的可访问性
标记为internal的成员对程序集内部的所有类可见,但对程序集外部的类不可见
对应上图的下边
-> 受保护内部成员的可访问性
标记为protected internal的成员对所有继承该类的类以及所有程序集内部的类可见。
注意:允许访问的集合是protected修饰符允许的类的集合加上internal修饰符允许的类的集合的集合。注意,这是protected和internal的联合,不是交集
对应上图:除了左上
-> 成员访问修饰符的总结
private 只在类的内部可访问
internal 对该程序集所有类可访问
protected 对所有继承该类的类可访问
protected internal 对所有继承该类或在改程序集内声明的类可访问
public 对任何类可访问
派生指继承声明该成员的类的类。
非派生指不继承声明该成员的类的类
-> 抽象成员
抽象成员是被设计来覆写的函数成员。特征如下:
1、它被用abstract修饰符标记
2、它没有实现代码块。抽象成员的代码块用分号表示
抽象成员只可以在抽象类中声明。
可以声明为抽象的类型:
方法、属性、事件、索引
关于抽象成员的其他重要事实:
1、尽管抽象成员必须在派生类中用相应的成员覆写,但不能把virtual修饰符附加到abstract修饰符
2、就像虚成员,派生类中抽象成员的实现必须指定override修饰符
虚函数和抽象成员比较:
关键字 virtual abstract
实现体 有实现体 没有实现体,被分号取代
在派生类中被覆写 能被覆写,使用override 必须被覆写,使用override
成员的类型 方法、事件 方法、事件
属性、索引 属性、索引
-> 抽象类
抽象类就是被设计类被继承的。抽象类只能被用做其他类的基类
1、不能创建抽象类的实例
2、抽象类使用abstract修饰符声明
3、抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带实现的成员的任意组合
4、抽象类自己可以派生自另一个抽象类
5、任何派生自抽象类的类必须使用override关键字实现该类所有的抽象成员,除非派生类自己也是抽象类
-> 密封类
-> 密封类使用sealed修饰符标注
-> 密封类只能被用作独立的类,它不能被用作基类
-> 静态类
静态类中,所有成员都是静态的。静态类用于分组不受实例数据影响的数据和函数。静态类的一个普通的用途可能是创建一个包含一组数学方法和值的数序库
-> 类本身必须标记为static
-> 类的所有成员必须是静态的
-> 类可以有一个静态构造函数,但没有实例构造函数,不能创建该类额实例
-> 静态类是显式是密封的,也就是说,不能继承静态类
-> 扩展方法
C#3.0的扩展方法特征,允许编写和声明它的类之外的类关联的方法。
扩展方法重要的需求:
-> 声明扩展方法的类必须声明为static
-> 扩展方法本身必须声明为static
-> 扩展方法必须包含关键字this作为它的第一个参数类型,并在后面跟着它所扩展的类的名称
static classExtendMyData
{
static Average(this MyData md) // 把该方法(Average)变为;类MyData的扩展方法
}
-> 类继承
1、可以使用一个已经存在的类作为新类的基础。已存在的类称为基类,新类称为派生类
-> 派生类成员;
1、自己声明中的成员
2、基类的成员
2、要声明一个派生类,需要在类名后加入基类规格说明。基类规格说明由冒号和后面跟着用作基类的类的名称组成。派生类被描述为直接继承自列出的基类。
3、派生类被描述为扩展它的基类,因为它包含了基类的成员,加上在它自己的声明中的任何附加功能
4、派生类不能删除他所继承的任何成员
-> 访问继承的成员
继承的成员可以被访问,就像它们是派生类自己声明的一样
所有类都派生自object类
1、一个类声明的基类规格说明中只能有一个单独的类。这称为单继承
2、虽然类只能直接继承一个基类,但继承的层次没有限制。也就是说,作为基类的类可以派生自另外一个类,而它又派生自另外一个类,一直下去,直至最终到object
-> 隐藏基类的成员
-> 要隐藏一个继承的数据成员,须要声明一个新的相同类型的成员,并使用相同的名称
-> 通过派生类中声明新的带有相同签名的函数成员,可以隐藏或掩盖继承的函数成员。
请记住,签名由名称和参数列表组成,但不包括返回类型
-> 要让编译器知道你在故意隐藏继承的成员,使用new修饰符。
-> 也可以隐藏静态成员
-> 基类访问
如果派生类必须完全地访问本隐藏的继承成员,可以使用基类访问表达式访问隐藏的继承成员。基类访问表达式又关键字base后面跟着一个点和成员的名称组成。
-> 使用基类的引用
派生类的实例由基类的实例加上派生类附加的成员组成。派生类的引用指向整个对象,包括基类部分
如果有一个派生类对象的引用,就可以获取该对象基类部分的引用,使用类型转换运算符把该引用转换为基类类型
-> 虚方法和覆写方法
使用基类引用访问派生类对象时,得到的是基类的成员。虚方法可以是基类的引用访问升至派生类内。
可以使用基类引用调用派生类的方法:
1、派生类的方法和基类的方法有相同的签名和返回类型
2、基类的方法使用virtual标注
3、派生类的方法使用override标注
-> virtual和override修饰符的重要信息:
1、覆写和被覆写的方法必须有相同的可访问性。
2、不能覆写static方法或非虚方法
3、方法、属性和索引,以及事件,它们都可以被声明为virtual和override
-> 覆写标记为override的方法
覆写方法可以在继承的任意成次出现
1、当使用对象基类部分的引用调用一个覆写的方法时,方法的调用被沿派生层次上溯执行,一直到标记为override的方法的最派生版本
2、如果在更高层的派生级别有改方法的其他成员,但没有被标记为override,那么它们不会被调用。
例如:
class MyBaseClass //基类
包含一个virtual 同名方法
class MyDerivedClass:MyBaseClass //派生类
包含一个override 同名方法
class SecondDerived:MyDerivedClass //最派生类
情况一:
包含一个override同名方法:
主函数中:
SecondDerived derived = new SecondDerived();
MyBaseClass mybc = (SecondDerived)derived;
derived.Print();
mgbc.Print();
结果:都调用SecondDerived中的Print()方法
情况二:
包含一个new 同名方法:
主函数同上:
结果:先调用SecondDerived中的Print()方法
再调用MyDerivedClass中的Print()方法
-> 构造函数的执行
-> 要创建对象的基类部分,基类的一个构造函数被作为创建实例过程的一部分被调用
-> 继承层次链中的每个类在执行它自己的构造函数体之前先执行它的基类的构造函数
-> 构造函数执行顺序:
1、初始化实例成员
2、调用基类构造函数
3、执行实例构造函数的方法体
警告:在构造函数中调用虚方法是既不推荐的。在基类的构造函数被执行时,基类的虚方法会调用派生类的覆写方法,但这是在派生类的构造函数方法体被执行之前。因此,调用会在派生类没有完全初始化之前传递到派生类
-> 构造函数初始化语句
有两种形式的构造函数初始化语句
1、使用关键字base并指明使用哪一个基类构造函数
2、使用关键字this并指明应该使用当前类的哪一个另外的构造函数
基类构造函数初始化放在冒号后面,冒号紧跟着类的构造函数声明的参数列表。构造函数初始化语句由关键字base和要调用的街垒构造函数的参数列表组成
当声明一个不带构造函数初始化语句的构造函数时,它是带有base()组成的构造函数初始化语句的形式的缩写
class MyDerived:MyBase{MyDerive(){}}
class MyDerived:MyBase{MyDerive():base(){}}
另一种形式的构造函数初始化语句可以让构造过程使用当前类中其他的构造函数
public MyClass(int x):this(x,"Using Default String"){...}
这种语法很有用的另一种情况是,一个类有好几个构造函数,并且它们都需要在对象够早的过程开始执行一些公共的代码。好处:进啊少了代码的重复。这也是推荐的做法。
不好的做法:声明一个方法来进行这些公共的初始化,让所有构造函数调用这个方法。
原因:首先编译器知道方法是构造函数后会进行一些优化。
其次有的时候一些事情必须在构造函数中进行,例如readonly字段只可以在构造函数中初始化。如果尝试在其他方法中初始化,会得到一个编译错误。
回到公共构造函数,如果这个构造函数可以初始化类中所有需要初始化的东西,并且可以独立用作一个有效的构造函数,那么可以把它设置为public的构造函数。
但是如果它不能完全初始化一个对象,必须禁止类的外部调用构造函数,因为那样的话它只会初始化对象的一部分。要避免这个问题,你可以把构造函数声明为private。在可以被类的外部调用的构造函数中添加:this()
-> 类访问修饰符
术语可见的有事用作术语可访问的。他们可以互换使用。类的可访问性有两个级别:public和internal
1、标记为public的类可以被系统内任何程序集中的代码访问。
2、标记为internal的类只能被它自己所在的程序集内的类看到
-> 这是默认的可访问级别,所以,除非在类的声明中显式地指定修饰符public,程序集外部的代码不能访问该类
-> 可以使用internal访问修饰符显式地声明一个类为内部的
-> 程序集间的继承
1、基类必须被声明为public,这样他才能被从它所在的程序集外部访问
2、必须在VS工程中包括对包含该积累的程序集的引用。
为了使引用其他程序集中的类和类型更容易,不使用它们的完全限定名称,在源文件的顶部放置一个using指令,并带上将要访问的类或类型所在的命名空间。
说明:增加对其他程序集的引用和增加using指令是两回事。增加对其他程序集的引用是告诉编译器所需的类型在哪里被定义。增加using指令允许你引用其他的类而不必使用它们的完全限定名称。
-> 成员访问修饰符
-> 所有显式声明在类的声明中的成员都是互相可见的,无论它们的访问性说明如何。
-> 5个成员访问级别:
共有的、私有的、受保护的、内部的、受保护内部的
-> 必须对每个成员指定访问级别。如果不指定某个成员的访问级别,它的隐式访问级别为private
-> 成员不能比它的类更可访问。也就是说,如果一个类的可访问性限于它所在的程序集,那么类的成员个体也不能从程序集的外部看到,无论它们的访问修饰符是什么,即使是public
-> 访问成员的区域
-> 访问类别基于两个关于正在声明的类的特征
1、类是否派生自正在声明的类
2、类是否和正在声明的类在同一程序集
-> 共有成员的可访问性
public访问级别是限制性最少的,所有的类,包括程序集内部的类和外部的类都可以自由地访问成员。
对应上图中的右下
-> 私有成员的可访问性
私有访问成员级别是限制最严格的
1、private类成员只能被它自己的类的成员访问。它不能被其他的类访问,包括继承他的类
2、然而,private成员能被嵌套在它的类中的成员访问
对应上图右下的member
-> 受保护成员的可访问性
protected访问级别如同private访问级别,除了一点,它允许派生自该类的访问该成员。
对应上图的右边
-> 内部成员的可访问性
标记为internal的成员对程序集内部的所有类可见,但对程序集外部的类不可见
对应上图的下边
-> 受保护内部成员的可访问性
标记为protected internal的成员对所有继承该类的类以及所有程序集内部的类可见。
注意:允许访问的集合是protected修饰符允许的类的集合加上internal修饰符允许的类的集合的集合。注意,这是protected和internal的联合,不是交集
对应上图:除了左上
-> 成员访问修饰符的总结
private 只在类的内部可访问
internal 对该程序集所有类可访问
protected 对所有继承该类的类可访问
protected internal 对所有继承该类或在改程序集内声明的类可访问
public 对任何类可访问
派生指继承声明该成员的类的类。
非派生指不继承声明该成员的类的类
-> 抽象成员
抽象成员是被设计来覆写的函数成员。特征如下:
1、它被用abstract修饰符标记
2、它没有实现代码块。抽象成员的代码块用分号表示
抽象成员只可以在抽象类中声明。
可以声明为抽象的类型:
方法、属性、事件、索引
关于抽象成员的其他重要事实:
1、尽管抽象成员必须在派生类中用相应的成员覆写,但不能把virtual修饰符附加到abstract修饰符
2、就像虚成员,派生类中抽象成员的实现必须指定override修饰符
虚函数和抽象成员比较:
关键字 virtual abstract
实现体 有实现体 没有实现体,被分号取代
在派生类中被覆写 能被覆写,使用override 必须被覆写,使用override
成员的类型 方法、事件 方法、事件
属性、索引 属性、索引
-> 抽象类
抽象类就是被设计类被继承的。抽象类只能被用做其他类的基类
1、不能创建抽象类的实例
2、抽象类使用abstract修饰符声明
3、抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带实现的成员的任意组合
4、抽象类自己可以派生自另一个抽象类
5、任何派生自抽象类的类必须使用override关键字实现该类所有的抽象成员,除非派生类自己也是抽象类
-> 密封类
-> 密封类使用sealed修饰符标注
-> 密封类只能被用作独立的类,它不能被用作基类
-> 静态类
静态类中,所有成员都是静态的。静态类用于分组不受实例数据影响的数据和函数。静态类的一个普通的用途可能是创建一个包含一组数学方法和值的数序库
-> 类本身必须标记为static
-> 类的所有成员必须是静态的
-> 类可以有一个静态构造函数,但没有实例构造函数,不能创建该类额实例
-> 静态类是显式是密封的,也就是说,不能继承静态类
-> 扩展方法
C#3.0的扩展方法特征,允许编写和声明它的类之外的类关联的方法。
扩展方法重要的需求:
-> 声明扩展方法的类必须声明为static
-> 扩展方法本身必须声明为static
-> 扩展方法必须包含关键字this作为它的第一个参数类型,并在后面跟着它所扩展的类的名称
static classExtendMyData
{
static Average(this MyData md) // 把该方法(Average)变为;类MyData的扩展方法
}