1. 继承的类型
- C#类可以派生自另一个类和任意多个接口。
- 结构总派生自System.ValueType,故不支持实现继承,但支持接口继承,即可以派生自任意多个接口。
- 类总派生自System.Object或用户选择的另一个类,还可以派生自任意多个接口。
- 如果类和接口都用于派生,则类总是必须放在接口的前面。
2.实现继承
(1) 虚方法:
- 基类方法或属性声明命名为virtual,任何派生类中使用override显示声明来重写该方法或属性。
- 成员字段和静态函数不能声明为virtual,因为虚方法只对类中的实例函数成员有意义。
(2) 多态性:
- 方法中接收基类作为参数,则任何派生自基类的类型都可以传递给该方法。
- 方法中使用参数的方法,如不是虚拟参数或没有重写派生类的方法,则执行基类方法;否则执行派生类方法。
(3) 隐藏方法:(尽量不使用)
- 若签名相同的方法,在基类和派生类中都进行了声明,但该方法没有分别声明为virtual和override,派生类方法就会隐藏基类方法。
- 使用new关键字隐藏方法,避免出现编译警告,用于处理版本冲突。
(4) 调用方法的基类版本:使用base关键字,在派生类中调用基类方法。
(5) 抽象类和抽象方法:
- 类和方法可以使用abstract关键字声明为抽象类和抽象方法。
- 类中包括抽象方法,则该类也必须声明为抽象类。
- 抽象类不能实例化;抽象方法不能直接实现,必须在非抽象的派生类中重写(关键字override);派生类中需要实现所有抽象成员。
- 虽然抽象类不能实例化,但可以声明一个抽象类变量,由实例化的派生类将其分配给基类。
// 抽象基类 Shape public abstract class Shape { public abstract void Resize(int width, int height); //抽象方法 } // 派生类 Ellipse public class Ellipse : Shape { public override void Resize(int width, int height) { Size.Width = width; Size.Height = height; } } //实例化 Shape s1 = new Ellipse();
(6) 密封类和密封方法:sealed修饰符
- 密封类表示不允许创建该类的子类。
- 密封方法表示不能重写该方法。
- 常用密封方法来终止继承:方法可以是基类的重写方法,但其继承类中不能扩展该方法。
class MyClass: MyBaseClass { public sealed override void FinalMethod() { // implementation } } class DerivedClass: MyClass { public override void FinalMethod() //wrong!Will give compilation error { } }
(7) 派生类的构造函数:
- 对于派生类,需保证通过层次结构进行构造。
- 构造函数总是按照层次结构的顺序调用:先调用System.Object类的构造函数 -> 再按照层次结构由上向下进行 -> 直到到达编译器要实例化的类为止。
// 使用自动属性初始化器初始化属性 // 编译器会创建一个默认的构造函数 public class Sharp { public Position Position { get; } = new Position(); public Size Size { get; } = new Size(); } // 派生类Ellipse public class Ellipse : Sharp { public Ellipse() : base() { } } // 实例化Ellipse类,构造函数调用顺序为 // Object 构造函数 -> Sharp 构造函数 -> Ellipse 构造函数
- 使用构造函数初始化器调用带参数的基类构造函数的赋值方式
// 基类构造函数内进行赋值,非默认初始化 public class Shape { public Shape(int width, int height, int x, int y) { Size = new Size { Width = width, Height = height }; Position = new Position { X = x, Y = y }; } } // 派生类 public class Ellipse : Shape { // 派生类中创建构造函数,用构造函数初始化器初始化基类的构造函数 public Ellipse(int width, int height, int x, int y) : base(width, height, x, y) { } // 希望允许使用默认的构造函数创建Ellipse对象 public Ellipse() : base( 0, height: 0, x: 0, y: 0) { } }
3. 接口:interface
- 接口只包括方法、属性、索引器和事件的声明。
- 不允许提供接口中任何成员的实现方式。
- 比较接口和抽象类:
(1) 定义和实现接口
- 接口的命名采用Hungarian表示法,即在名称前加一个字母,表示所定义对象的类型。接口类型字母为I。
public interface IBankAccount { void PayIn(decimal amount); bool Withdraw(decimal amount); decimal Balance { get; } }
// 派生类 SaverAccount public class SaverAccount : IBankAccount { private decimal _balance; public void PayIn(decimal amount) => _balance += amount; public bool Withdraw(decimal amount) { if (_balance >= amount) { _balance -= amount; return true; } WriteLine("Withdrawal attempt failed."); return false; } public decimal Balance => _balance; public override string ToString() => $"Venus Bank Saver: Balance = {_balance,6:C}"; } // 派生类 GoldAccount public class GoldAccount : IBankAccount { //etc }
- 接口仅表示其成员的存在性,派生的类负责确定这些成员是虚拟还是抽象的(但只有在类本身是抽象的,这些函数才能是抽象的)。
- 接口引用安全可以按成类引用——但接口引用强大之处在于,它可以引用任何实现该接口的类。
IBankAccount[] accounts = new IBankAccount[2]; account[0] = new SaverAccount(); account[1] = new GoldAccount();
(2) 派生的接口
- 接口可以彼此派生,实现派生接口的类必须实现相关接口的所有方法。
public interface ITransferBankAccount : IBankAccount { bool TransferTo(IBankAccount destination, decimal amount); }
public class CurrentAccount : ITransferBankAccount { private decimal _balance; public void PayIn(decimal amount) => _balance += amount; public bool Withdraw(decimal amount) { if (_balance >= amount) { _balance -= amount; return true; } WriteLine("Withdrawal attempt failed."); return false; } public decimal Balance => _balance; public bool TransferTo(IBankAccount destination, decimal amount) { bool result = Withdraw(amount); if (result) { destination.PayIn(amount); } return result; } public override string ToString() => $"Jupiter Bank Current Account: Balance = {_balance,6:C}"; }
- 在实现并调用派生接口的方法时,不必知道该对象类型,只需要知道该对象实现了接口即可。
4. is 和 as 运算符: 与继承有关
- as运算符:返回对象的引用,且不抛出InvalidCastException异常;如不是所需要类型,返回null。
public void WorkWithManyDifferentObject(object o) { IBankAccount account = o as IBankAccount; if (account != null) { //work with the account } }
- is运算符:根据对象是否使用指定的类型,返回true或false。
public void WorkWithManyDifferentObject(object o) { if (o is IBankAccount) { IBankAccount account = (IBankAccount)o; //work with the account } }
注:文中代码均来自《C#高级编程(第10版)C# 6 & .NET Core 1.0》