抽象类和抽象成员
- 使用abstract声明的类是抽象类
 - 抽象类不可以被实例化,只有其具体的子类(不是抽象类)才可以实例化
 - 抽象类可以定义抽象成员
 - 抽象成员和virtual成员很像,但是不提供具体的实现。子类必须提供实现,除非子类也是抽象的
 
public abstract class AbstractClass{public decimal TestValue { get; }public abstract decimal NetValue { get; }}public class SonClass : AbstractClass{public long SharesOwned;public decimal CurrentPrice;//必须重写抽象成员,使用Override关键字重写,和virtual类似public override decimal NetValue => CurrentPrice * SharesOwned;}
隐藏被继承的成员(隐藏父类成员)
- 父类和子类可以定义相同的成员:
public class A { public int IntCount = 1; }public class B : A { public int IntCount = 2; }
 
- 上面Class B中的Counter字段就隐藏了A里面的Counter字段(通常是偶然发生的)。例如子类添加某个字段之后,父类也添加了相同的一个字段。
 - 编译器会发生警告
 对隐藏成员是如何解析的
- 编译时,对A的引用会绑定到A.IntCount
 - 编译时,对B的引用会绑定到B.IntCount
public class A { public int IntCount = 1; }public class B : A { public int IntCount = 2; }public class Program{static void Main(){A a = new A();System.Console.WriteLine(a.IntCount);//变量类型A 输出1B b = new B();System.Console.WriteLine(b.IntCount);//变量类型B 输出2A x = new B();System.Console.WriteLine(x.IntCount);//变量类型A 输出1}}
 
如果故意隐藏父类的成员,可以在子类的成员前面加上new修饰符
这里new修饰符仅仅会抑制编译器的警告而已(就是告诉编译器我是故意这样写的,你别警告我了)
SEALED(密封)
针对重写的成员,可以使用sealed关键字把它“密封”起来,防止它被其子类重写
public sealed override decimal NetValue => CurrentPrice * SharesOwned;
也可以sealed类本身,就隐式的sealed所有的virtual函数了
BASE关键字
base和this略像,base主要用于:
从子类访问父类里被重写的函数(virtual关键字修饰的)
调用父类的构造函数
下面这种写法可保证,访问一定是父类的属性,无论该属性是被重写还是被隐藏了
class classParent { public virtual int property { get; } }class classChild : classParent{public int childProperty { get; }public override int property => base.property + childProperty;}
构造函数和继承
子类必须声明自己的构造函数
从子类可访问父类的构造函数,但不是自动继承的
子类必须重新定义它想要暴露的构造函数
(基本的意思就是子类的写自己的构造函数,可调用父类的构造函数)
一些具体的代码
class BaseClass{public int X;public BaseClass() { System.Console.WriteLine("Hello"); }public BaseClass(int x) { this.X = x; }public BaseClass(string x) { System.Console.WriteLine(x); }}class SubClass : BaseClass{//public SubClass(int x) : base(x){}public SubClass(string x) : base(x) { }static void Main(){SubClass sub = new SubClass("SubClass"); // print :SubClass}}
隐式调用无参的父类构造函数
如果子类的构造函数里没有使用base关键字,那么父类的无参构造函数会被隐式的调用
using System;class BaseClass{public int X;public BaseClass() { X = 1; }}class SubClass : BaseClass{public SubClass() { Console.WriteLine(X); } // print:1static void Main(){SubClass sub = new SubClass();}}
如果父类没有无参构造函数,那么子类就必须在构造函数里使用base关键字来调用父类的其它构造函数这样才行
 
构造函数和字段初始化顺序
对象被实例化时,初始化动作按照如下顺序进行:
- 从子类到父类:
- 字段被初始化
 - 父类构造函数的参数值被算出
 
 - 从父类到子类
- 构造函数体被执行
 
 
重载和解析
例子
class BaseClass { }class SubClass : BaseClass { }class program{// 重载static void Method(BaseClass baseclass) { }static void Method(SubClass subclass) { }}
- 从子类到父类:
 
重载方法被调用时,更具体的类型有用更高的优先级
class BaseClass { }class SubClass : BaseClass { }class program{// 重载static void Method(BaseClass baseclass) { }static void Method(SubClass subclass) { }static void Main(){SubClass sub = new SubClass();Method(sub); // 调用 Method(SubClass)BaseClass bc = new SubClass();Method(bc); // 调用 Method(BaseClass)}}

