第四章 继承
1.实现继承和接口继承
- 实现继承:表示一个类派生于一个基类型,他拥有该基类型的所有成员字段和函数,在实现继承中,派生类采用基类型的每个函数的实现代码,除非在派生类型的定义中指定重写某个函数的实现代码。在需要给现有类型添加功能,或许多相关的类型共享一组重要的公共功能时,这种类型的继承非常重要。
- 接口继承:表示一个类型只继承了函数的签名,没有继承任何实现代码。在需要指定该类型具有某些可用的特性时,最好使用这种类型的继承。
2.多重继承
C#不支持多重继承。而c#又允许类型派生自多个接口-----多重接口继承。这说明c#可以派生自另一个类和任意多个接口。
3.结构和类
结构是值类型,类是引用类型。使用结构的一个限制是结构不支持继承,但每个结构都派生自System.ValueType。但结构可以实现接口。
4.虚方法
把一个基类函数声明为Virtual,就可以在任何派生类中重写该函数。也可以把属性声明为virtual。
C#中虚函数的概念与OOP(面向对象编程)的概念相同:可以在派生类中重写虚函数。在调用方法时,会调用该类对象的合适方法。在c#中,函数默认不是虚的,但(除了构造函数以外)可以显示的声明为虚函数,在派生类的函数重写另一个函数时,要使用override关键字显示声明。
成员字段和静态函数都不能声明为virtual。
5.隐藏方法
如果签名相同的方法在基类和派生类中都进行了声明,但该方法没有分别声明virtual和override,派生方法就会隐藏基类方法。
虚方法与隐藏方法的区别:
虚方法:将一个基类函数声明为virtual,就可以在任何派生类中重写该函数。
class MyBaseClass
{
public virtual string VirtualMethod()
{
return "Method is called in base class."
}
}
在派生类中进行重写该函数时,需要使用override关键字显示声明
class MyDerivedClass : MyBaseClass
{
public override string VirtualMethod()
{
return "Method is called in derived class."
}
}
当在子类中重写虚函数之后,不管在哪里进行调用,调用的都是重写后的方法(相当于原先的方法被重写后的方法给覆盖了)。
隐藏和重写的区别:
隐藏:只是将父类中的方法给隐藏了,实际这个方法还存在。
重写:将原先父类中的方法完全重写了,原先的方法是不存的了。
//隐藏方法:如果使用子类声明的对象,则调用隐藏方法会调用子类的;如果使用父类声明的对象,则会调用父类中的隐藏方法。
//父类:Enemy
//子类:Boss
//Move()是一个隐藏方法
class Enemy
{
public string Move()
{
return "This is Move from Enemy!";
}
}
class Boss : Enemy
{
public new string Move()
{
return "This is Move from Boss!";
}
}
Boss boss = new Boss();
boss.Move(); //This is Move from Boss!
Enemy boss = new Boss();
boss.Move(); //This is Move from Enemy! //构造一样,但是声明不一样,调用的隐藏方法也是不一样的。
一般不去使用隐藏方法,因为很容易引起方法调用的混乱。
---------------------
原文:https://blog.csdn.net/lym940928/article/details/79833406
6.调用函数的基类版本
语法:base.BaseMethodName()
7.抽象类和抽象函数
C#允许把类和函数声明称Abstract。抽象类不能实例化,而抽象函数不能直接实现,必须在非抽象的派生类中重写。显然,抽象函数本身也是虚拟的。如果类包含抽象函数,那么该类也是抽象的,也必须声明为抽象。
8.密封类与密封方法
C#允许把类和方法声明为sealed。对于类,这表示不能继承该类,对于方法,表示不能重写该方法。
9.派生类的构造函数
构造函数的调用顺序是先调用System.Object,再按照层次结构由上向下进行,直到到达编译器要实例化的类为止。还要注意在这个过程中,每个构造函数都初始化他自己的类中的字段。
注意构造函数的执行顺序。最先调用的总是基类的构造函数。也就是说,派生类的构造函数可以在执行过程中调用他可以访问的任何基类的方法、属性和任何其他成员变量,因为基类已经构造出来了。
- 在层次结构中添加无参数的构造函数
public abstract class SmallCar
{
private string description;
public SmallCar():base()
{
description=”This is SmallCar”;
}
}
在构造函数中添加了对基类构造函数的调用,这次使用的关键字是base而不是this,表示这是基类的构造函数,而不是当前类的构造函数。在base关键字后面的圆括号里没有参数,因为没有给基类构造函数传递任何参数。
如果编译器没有在左花括号前面找到另一对构造函数的任何引用,他就会假定我们要调用的是基类的构造函数。
注:如果构造函数为私有的,会造成编译错误,该错误不会发生在基类中,而出现在他的派生类中。
- 在层次结构中添加有参数的构造函数
public abstract class SmallCar
{
private string description;
public SmallCar(string description)
{
This.description=description;
}
}
public class miniCar:SmallCar
{
public miniCar(string description)
: base(description)
{
}
}
miniCar本身不能初始化description,但是可以把他传递给基类,以便基类构造函数进行处理,它本身不需要任何操作。
10.修饰符
修饰符 |
应用于 |
说明 |
Public |
所有类型或成员 |
任何代码均可以访问该项 |
Protected |
类型和内嵌类型的所有成员 |
只有派生类能访问该项 |
Internal |
所有类型或成员 |
只能在包含他的程序集中访问该项 |
Private |
类型和内嵌类型的所有成员 |
只能在它所属的类型中访问该成员 |
Protected internal |
类型和内嵌类型的所有成员 |
只能在包含它的程序集和派生类型的任何代码中访问该项 |
类型定义可以是共有的也可以是私有的,但不能把类型定义为protected、private和protected internal,因为这些修饰符对于包含在命名空间中的类型没有意义。但是这些类型可以应用于嵌套的类型(即包含在其他类型中的类型)。如果有嵌套类型,则内部类型总是可以访问外部类型的所有成员。
public class OutClass
{
protected class InnerClass
{
//
}
//
}
其他修饰符
修饰符 |
应用于 |
说明 |
New |
函数成员 |
成员用相同的签名隐藏继承的成员 |
Static |
所有成员 |
成员不作用于类的具体实例 |
Virtual |
仅函数成员 |
成员可由派生类重写 |
Abstract |
仅函数成员 |
虚拟成员定义了成员的签名,但没有提供实现代码 |
Override |
仅函数成员 |
成员重写了继承的虚拟或抽象成员 |
Sealed |
类、方法和属性 |
对于类,不能继承自密封类。对于属性和方法,成员重写已继承的虚拟成员,但任何派生类中的任何成员都不能重写该成员。该修饰符必须与override一起使用 |
Extern |
仅静态方法 |
成员在外部用另一种语言实现 |