一、继承概念
继承类型分为两种: 实现继承和接口继承。
多重继承: 一个类派生自多个类。
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 节点中添加包含该基类的程序集的引用。