一、继承概念
继承类型分为两种: 实现继承和接口继承。
多重继承: 一个类派生自多个类。
C# 不支持多重实现继承,但允许多重接口继承。
结构不支持继承: 结构不支持实现继承,但是可以支持接口继承。
- 结构总是派生自 System.ValueType,它们还可以派生自任意多个接口。
- 类总是派生自 System.Object 或用户选择的另一个类,它们还可以派生自任意多个接口。
二、对基类的访问和引用
基类访问
class SomeClass{public string Field1 = "Field1 -- In the base class";}class OtherClass : SomeClass{new public string Field1 = "Field1 -- In the derived class";public void PrintField1(){Console.WriteLine(Field1); // 访问派生类Console.WriteLine(base.Field1); //访问基类}}class Program{static void Main(string[] args){OtherClass oc = new OtherClass();oc.PrintField1();Console.ReadKey();}}
使用基类的引用
class MyBaseClass{public void Print(){Console.WriteLine("This is the base class.");}}class MyDerivedClass : MyBaseClass{public int var1;new public void Print(){Console.WriteLine("This is the derived class.");}}class Program{static void Main(string[] args){MyDerivedClass derived = new MyDerivedClass();MyBaseClass mybc = (MyBaseClass)derived;derived.Print();mybc.Print();Console.ReadKey();}}
输出结果:
Field1 – In the derived class
Field1 – In the base class
三、虚方法
1、声明定义虚方法
把一个基类函数声明为 virtual,就可以在任何派生类中重写该函数。
class MyBaseClass{//虚方法public virtual string VirualMethod(){return "This method is virtual and defined in MyBaseClass";}//虚属性public virtual string ForeName{get { return foreName; }set { foreName = value; }}private string foreName;}class MyDerivedClass : MyBaseClass{//使用 override 来 重写虚方法public override string VirualMethod(){return "This method is an override define in MyDerivedClass";}}class Program{static void Main(string[] args){MyDerivedClass derived = new MyDerivedClass();MyBaseClass mybc = (MyBaseClass)derived;Console.WriteLine(derived.VirualMethod());Console.WriteLine(mybc.VirualMethod());Console.ReadKey();}}
输出结果:
This method is an override define in MyDerivedClass
This method is an override define in MyDerivedClass
从结果可知,派生类转换为基类,其结果仍是调用派生类重写的虚方法。
2、调用函数的基类版本
若派生类在基类的基础上,对虚方法进行代码逻辑的补充时:
class CustomerAccount{public virtual decimal CalculatePrice(){return 0.0M;}class GoldAccount : CustomerAccount{public override decimal CalculatePrice(){return base.CalculatePrice() * 90;}}}
3、虚方法小结:
- 在 C# 中,函数在默认情况下不是虚拟的,但(除了构造函数以外)可以显式地声明为 virtual。
- C# 要求在派生类的函数重写另一个函数,要使用 overiride 关键字显式声明。
- 成员字段和静态函数都不能声明为 virual。
- 虚方法不可用 private 修饰符。
四、隐藏方法
如果在基类和派生类中,有签名相同的方法,但该方法没有分别声明为 virrual 和 override,派生类方法就会隐藏基类方法。
隐藏方法存在安全隐患: 由于派生类和基类使用的是相同签名的方法,但该方法执行的结果不相同。若程序员想要调用派生类的,但不小心调用的是基类的,该方法执行后会出现潜在程序的错误。
五、抽象类与抽象函数
- C# 允许把类和函数声明为 abstract。
- 抽象类不能实例化,抽象函数不能直接实现,必须在非抽象的派生类中重写。
- 抽象函数本身也是虚拟的。
- 如果类包含抽象函数,则该类也是抽象的,也必须声明为抽象的。
abstract class Building
{
public abstract decimal CalculateHeatingCost();// abstract method
}
六、密封类和密封方法
C# 允许把类和方法声明为 sealead。对于类,这表示不能继承该类;对于方法(属性也可密封),这表示不能重写该方法。(密封方法,是否可以使得隐藏方法更加安全)
1、密封类:
sealed class FinalClass{}//提示错误,密封类不能被继承class DerivedClass : FinalClass{}
2、密封方法
class MyBaseClass1{public virtual void TestFun(){}}class FinalClass : MyBaseClass1{sealed public override void TestFun(){}}class DerivedClass : FinalClass{//提示错误,密封方法不能被重写//因为继承的这个基类设置该方法为密封方法public override void TestFun(){}}
七、可见性修饰符
表-修饰符的访问说明
修饰符 | 应用于 | 说明 |
---|---|---|
public | 所有类或成员 | 任何代码均可以访问该项 |
protected | 类型和内联类型的所有成员 | 只有派生的类型能访问该项 |
internal | 所有类型或成员 | 只能在包含它的程序集中访问该项 |
private | 类型和内联类型的所有成员 | 只能在它所属的类型中访问该项 |
protected internal | 类型和内联类型的所有成员 | 只能在包含它的程序集和派生类型的任何代码中访问该项 |
内嵌类型: 在类的内部再声明一个类
public class OtherClass{private int id = 0;protected class InnerClass{public void InnerClassFun(){OtherClass oc = new OtherClass();oc.id = 0;//可访问}}}
表-成员可访问性总结(出自《C# 图解教程》-8.9.7 成员访问修饰符小结)
同一程序集内的类 | 同一程序集内的类 | 不同一程序集内的类 | 不同一程序集内的类 | |
---|---|---|---|---|
非派生类 | 派生类 | 非派生类 | 派生类 | |
private | ||||
internal | ✅ | ✅ | ||
protected | ✅ | ✅ | ||
protected internal | ✅ | ✅ | ✅ | |
public | ✅ | ✅ | ✅ | ✅ |
其他修饰符
修饰符 | 应用于 | 说明 |
---|---|---|
new | 函数成员 | 成员用相同的签名隐藏继承的成员 |
staitc | 所有成员 | 成员不作用于类的具体实例 |
virtual | 仅函数成员 | 成员可以由派生类重写 |
abstract | 仅函数成员 | 虚拟成员定义了成员的签名,但没有提供实现代码 |
override | 仅函数成员 | 成员重写了继承的虚拟或抽象成员 |
sealed | 类、方法和属性 | 对于类,不能继承自密封类。对于属性和方法,成员重写已继承的虚拟成员,但任何派生类中的任何成员都不能重写该成员。该修饰符必须与 override 一起使用 |
extern | 仅静态(DllImport)方法 | 成员在外部用另一种语言实现 |
八、程序集间的继承
C# 允许从一个在不同的程序集内定义的基类来派生类。
要从不同程序集中定义的基类派生类,必须具备以下条件:
- 基类必须被声明为 public,这样才能从它所在的程序集外部访问它。
- 必须在 Viusal Studio 工程中的 Reference 节点中添加包含该基类的程序集的引用。