派生类型可直接赋值给基类型,这称为隐式转型,总是会成功,不会抛出异常,反之则不成立。从基类型转换成派生类型要求执行显式转型,而显式转型在运行时可能失败。
完全不相关的类型也能相互转换,关键是要在两个类型之间提供转型操作符。C#允许包含显式或隐式转型操作符。
class GPSCoordinates
{
public static implicit operator UTMCoordinates(GPSCoordinates coordinates)
{
// ...
}
}
派生类继承除构造函数和析构器之外的所有基类成员。但继承不意味着一定能访问,派生类不能访问基类的私有成员。
要从派生类中访问受保护的成员,必须能在编译时确定它是派生类中的实例。(这里有点长,建议看书上的解释)
扩展方法从技术上说不是类型的成员,所以不可继承。如扩展基类,在派生类中也能使用扩展方法。
C#的单继承是其在面向对象方面与C++主要区别之一。(C++的多继承被认为是一种bug,详细见另一笔记)
极少数需要多继承时,一般的解决方案是使用聚合(aggregation),不是继承,而是一个类包含另一个类的实例。
将类标记为sealed来避免非预期的派生。比如string类就是用sealed修饰的。
C#支持重写实例方法和属性,但不支持字段和任何静态成员的重写。基类需将允许重写的成员标记为virtual。
C++在构造期间不调度虚方法。相反,在构造期间,类型与基类类型关联,而不是与派生类型关联,虚方法调用的是基类的实现。C#则相反,会将虚方法调用调度给派生得最远的类型。但无论如何,在C#中应尽量避免出现这种情况。
虚暗示着实例,只有实例成员才可以是virtual的。
如果没有使用override关键字,编译器会生成警告消息,还可以使用new修饰符,这种情形称为脆弱的基类。
它在基类面前隐藏了派生类重写声明的成员,这时不是调用派生得最远的成员。相反,是搜索继承链,找到使用new修饰符的那个成员之前的、派生得最远的成员。(new把父类的成员隐藏了,而不是重写了,知道虚函数表的原理之后很好理解)
假如没有指定override,也没有指定new,默认就是new,从而维持版本的安全性。
对CIL来说,new修饰符对编译器生成的代码没有影响,但它会生成方法的newslot元数据特性,从C#角度看,唯一作用就是移除编译器警告。
为类使用sealed修饰符可禁止从该类派生。类似的,虚成员也可密封。
class A
{
public virtual void Method()
{
}
}
class B : A
{
public override sealed void Method()
{
}
}
class C : B
{
// ERROR: Cannot override sealed members
//public override void Method()
//{
//}
}
sealed修饰的成员需要从父类中继承并override,必须密封重写的成员
base的语法几乎和this完全一样。用override修饰的任何成员都自动称为虚成员,子类能进一步“特化”实现。
假如基类没有可访问的默认构造函数,就不知道如何构造基类,C#编译器将会报错。
为避免这种错误,程序员需要在派生类构造函数的头部显式指定要运行哪一个基类构造函数。
public Contact(string name) : base(name)
{
}
用abstract修饰,抽象成员自动为虚(因为必须被重写),但不能显式声明为虚,抽象成员不能声明为私有,否则派生类看不见它们。
与C#不同,C++使用“=0”表示纯虚函数,包含纯虚函数的类叫做抽象类,C++不要求类本身有任何特殊的声明。
多态性(polymorphism)是指同一个签名可以有多个实现。抽象成员是实现多态的一个手段。
即使类没有显式指明自己从object派生,也肯定从object派生。
is操作符的好处是能创建一个显式转型可能失败但又没有异常处理开销的代码路径。从C#7.0开始is操作符除了用于类型检查,也可以用于声明变量并且赋值。
可以将一批变量放在一个元组中,并用is操作符进行元组模式匹配,并可以对元组中的变量进行类型模式匹配和赋值。
属性模式匹配采用大括号而不是小括号来指定匹配值,其次,在属性模式匹配中,匹配值的顺序不重要。还会检查null值。
is操作符的属性模式匹配不支持判断表达式。
is操作符的优点是允许验证一个数据项是否属于特定类型,as操作符会像一次转型所作的那样,尝试将对象转换为特定数据类型。如果不能转换,as会返回null,它避免了可能因为转型而造成的异常。as仅适用于引用类型。