继承
继承
继承可以提高软件模块的可利用性和可扩展性,以提高软件开发效率
类的继承
C#类可以从一个类继承或实现多个接口
class Man : People
{
}
基类(父类),People是基类,同时也是一派生类,所有类都继承自Object类。派生类(子类),Man是子类,子类不但有自身的成员,还包含了基类的成员。
使用new隐藏基类的成员
显式地隐藏从基类继承的成员,你并非一定这样做,不用它也可以达到目的,但会引发一个编译器警告,同样地,如果你没有隐藏任何基类成员时,就不应该使用new修饰符,那样也会引发一个编译器警告。
如何隐藏:
- 数据成员:在子类中声明一个相同名称的成员,并使用new修饰,只需名称相同即可,无需类型也相同
- 方法成员:声明一个具有相同签名的方法,并使用new修饰
- 静态成员同上
class People
{
public int Age { get; set; }
public string Name { get; set; }
public static int Instances { get; set; }
public People()
{}
public People(int age, int name)
{
this.Age = age;
this.Name = name;
}
public void SayHi()
{
Console.WriteLine(${"Hi"{this.Name ?? ""}");
}
public static void SayHiStatic()
{
Console.WriteLine($"cnt:{Instances}");
}
}
class Student : People
{
public int SchoolName { get; set; }
//警告:Name隐藏了基类数据成员(且类型不同),但未使用new显式隐藏
public int Name { get; set; }
//警告:Grade未隐藏任何基类数据成员,但使用new修饰符
new public int Grade { get; set; }
//调用基类构造函数
//若此处不使用base(age, nmae),则会调用基类的无参构造函数
public Student(int age, int name, int schoolName, int grade) : base(age, name)
{
this.SchoolName = schoolName;
this.Grade = grade;
}
//显式隐藏基类方法成员,同时返回值不同
new public int SayHi()
{
//访问基类数据成员
Console.WriteLine($"age:{base.Age}, schoolName:{this.SchoolName}");
return 1;
}
//显式隐藏基类的静态方法
new public static void SayHiStatic()
{
// do nothing
}
}
类的初始化顺序
- 初始化类的实例字段
- 调用基类的构造函数,如无明确指定则调用Object构造函数
- 调用自己的构造函数
跨程序集的继承
程序集可以简单理解为类的物理组织形式,而命名究竟则可认为是类的逻辑组织形式。只有指定了类所在的程序集以及命名究竟,编译器才能“找到”该类型。一个程序集明确地说就是一个exe或dll。跨程序集继承其实只需要在当前程序集中添加对那个程序集的引用而已。
密封类(sealed)
当我们把一个类声明为密封类的时候,就意味着该类无法作为基类,也就是说它不可以被继承,使用sealed关键字可以把类声明为密封类。String类就是密封类。
静态类
静态类有如下特征:
- 不能使用sealed或abstract修饰符(不能被继承)
- 必须直接继承System.Object类型,即不能继承于其它类(不能继承)
- 不能实现任何接口
- 不能包含任何操作符
- 不能包含使用protected或者protected internal修饰符的静态成员(不能被继承)
- 只能包含静态成员
- 可以包含静态构造函数,但不能包含实例构造函数
- 不能被实例化
- 静态类是密封的,不能被继承(不能被继承)
静态类在加载包含该类的程序集时由CLR自动加载。
扩展方法
当我们需要对已有的类添加新的功能时,当然可在从已有的类继承,然后在派生类中加入新的方法,我们还可以使用扩展方法:
- 一种特殊的静态方法,它必须定义于静态类中
- 第一个参数以this修饰符为前缀,后跟要扩展的目标类型及其形参
- 扩展方法所在的类必须在使用它的类可见范围内,否则需要使用using指令将命名究竟显式导入
- 扩展方法只能针对实例调用,目标类不能为静态类
- 如果扩展方法和被扩展方法中某个方法签名相同,扩展方法永远都不会被调用
- 其它命名空间下的扩展方法的优先级低于当前命名空间下的扩展方法的优先级,优先级最高为实例方法
扩展方法的调用方式与实例对象的方法成员相似。
派生类型的向上转型
子类实际上是由基类的实例加上子类新增的成员组成,将子类向上转型就转换到它的基类类型,这种转换会让子类中新增的成员变得“不可见”。
参考引用
[1]: C# 权威指南