抽象类和抽象成员
- 使用abstract声明的类是抽象类
- 抽象类不可以被实例化,只有其具体的子类才可以实例化
- 抽象类可以定义抽象成员
- 抽象成员和virtual成员很像,但是不提供具体的实现。子类必须提供实现,除非子类也是抽象的
隐藏被继承的成员
- 父类和子类可以定义相同的成员:
public class A {public int Counter = 1;}
public class B : A {public int Counter = 2;}
- class B中的 Counter字段就隐藏了A里面的Counter字段(通常是偶然发生的)。例如子类添加某个字段之后,父类也添加了相同的一个字段。
- 编译器会发出警告
- 按照如下规则进行解析:
- 编译时对A的引用会绑定到A.Counter
- 编译时对B的引用会绑定到B.Counter
上面输出 1 2 1
- 如果想故意隐藏父类的成员,可以在子类的成员前面加上 new 修饰符
- 这里的 new 修饰符仅仅会抑制编译器的警告而已
public class A {public int Counter = 1;}
public class B : A {public new int Counter = 2;}
new vs override
sealed
针对重写的成员,可以使用sealed关键字把它“密封”起来,防止它被其子类重写
public sealed override decimal Liability{get{return Mortgage;}}
也可以sealed 类本身,就隐式的sealed所有的virtual函数了
base 关键字
base 和 this 略像,base主要用于:
- 从子类访问父类里被重写的函数
- 调用父类的构造函数
这种写法可保证,访问的一定是Asset的Liability属性,无论该属性是被重写还是被隐藏了
构造函数和继承
- 子类必须声明自己的构造函数
- 从子类可访问父类的构造函数,但不是自动继承的
- 子类必须重新定义它想要暴露的构造函数
- 调用父类的构造函数需要使用 base 关键字
- 父类的构造函数肯定会先执行
隐式调用无参的父类构造函数
- 如果子类的构造函数里没有使用base关键字,那么父类的无参构造函数会被隐式的调用
- 如果父类没有无参构造函数,那么子类就必须在构造函数里使用base关键字
构造函数和字段初始化顺序
- 对象被实例化时,初始化动作按照如下顺序进行:
从子类到父类:
- 字段被初始化
- 父类构造函数的参数值被算出
从父类到子类
- 构造函数体被执行
重载和解析
- 看例子:
static void Foo(Asset a){}
static void Foo(House h){}
重载方法被调用时,更具体的类型拥有更高的优先级
House h= new House(...);
Foo(h); //Calls Foo(House)
调用哪个重载方法是在编译时就确定下来的
Asset a=new House(...);
Foo(a); //Calls Foo(Asset);