代码重用归为两类:继承(is-a),包含/委托(has-a)
继承保护了封装。
C#要求一个类只能有一个直接基类。
sealed关键字:
如果我们将类标记为sealed,编译器将不会允许我们从这个类型派生。
C#结构总是隐式密封的。
如果对基类的虚函数【vritual/override修饰的】用sealed修饰,则,在此基类的派生类中,此虚函数不可以被override【即不允许派生类定义自己版本的实现】。
只要子类想访问由父类定义的公共或受保护的成员,就可以使用base关键字。
class A
{
public A(int a)
{...}
}
class B : A
{
public B(int a, int b)
:base(a)
{}
}
private类型成员,只能在所属类内部实现中使用。
protected类型成员,可以在所属类和其派生类内部实现从使用。
public类型成员,可以在所属类和其派生类内部实现被使用,也可被类或其派生类对象通过点操作符访问。
包含/委托编程
class BenefitPackage
{
public double ComputePayDeduction()
{
return 125.0;
}
}
class Employee
{
protected BenefitPackage empBenefits = new BenefitPackage();
}
如果要公开被包含对象的功能给外部世界,需要委托。
委托就是增加公共成员到包含类,以便使用被包含对象的功能。
嵌套类型:直接在类或结构作用域中定义类型【枚举,类,接口,结构或委托】。
被嵌套类型被认为是嵌套类的成员。可以像操作其它成员【字段,属性,方法,事件等】操作被嵌套类型。
public class OuterClass
{
public class PublicInnerClass{}
private class PrivateInnerClass{}
}
class Main
{
static void Main()
{
OuterClass.PublicInnerClass inner = new OuterClass.PublicInnerClass();
}
}
嵌套类型特征:
类型可用private修饰。
嵌套类型可以访问包含类的所有成员
嵌套类一般用作外部类的辅助设施。
1.
多态为子类提供了一种方式,使其可以定义由基类定义的方法。
vritual用于修饰基类的虚方法
override用于修饰派生类重写的基类的虚方法
class Employee
{
public virtual void GiveBonus(float amount)
{
Pay += amount;
}
...
}
class SalesPerson : Employee
{
public override void GiveBonus(float amount)//
{
base.GiveBouns(amount);
...
}
}
2.抽象基类
可以用abstract修饰类。不允许创建被abstract修饰类的对象。
抽象基类,可以定义抽象成员。
抽象成员:基类不提供默认实现,必须在派生类中提供实现。
abstract class Shape
{
public Shape(string name = "NoName")
{
PetName = name;
}
public string PetName{get; set;};
public virtual void Draw()
{
Console.WriteLine("Inside Shape.Draw()");
}
}
class Circle : Shape
{
public Circle(){}
public Circle(string name)
: base(name)
{}
}
class Hexagon : Shape
{
public Hexagon(){}
public Hexagon(string name)
: base(name)
{}
public override void Draw()
{
Console.WriteLine("Drawing {0} the Hexagon", PetName);
}
}
只能在抽象基类中定义抽象成员
abstract class Shape
{
public Shape(string name = "NoName")
{
PetName = name;
}
public string PetName{get; set;};
// 抽象方法不提供实现
public abstract void Draw();
}
如果基类中存在抽象方法,派生类要么对基类的抽象方法均进行override来提供具体实现。否则,派生类也将成为抽象类,且派生类需要用abstract修饰。
3.成员遮蔽
如果派生类定义的成员和定义在基类中的成员一致,派生类就遮蔽了父类的版本。
class Circle
{
public void Draw()
{}
public virtual void Draw(int num)
{...}
}
class ThreeDCircle : Circle
{
// 在此类内部,此类实例或此类的名字通过点操作符,此类的派生类内部,此类派生类实例或此类派生类的名字通过点操作符 访问Draw时,访问的将是此类的Draw。要访问此类基类的Draw,需要将类型转换为基类再访问。
// 编译时,会有警告。通过添加new修饰符可消除警告。
// 对成员字段同样适用。
public [new] void Draw()
{
...
}
public override void Draw(int num)
{...}
}
class Main
{
static void Main()
{
ThreeDCricle o = new ThreeDCircle();
// 执行ThreeDCricle::draw
// 点操作符访问类成员,若成员不是虚成员,依据左边类型静态类型,决定名字查找起始作用域。
// 若成员是虚成员,依据左边类型动态类型,决定名字查找起始作用域。
o.draw();
// 执行ThreeDCricle::draw
o.draw(10);
// 执行Circle::draw
// 因为(Circle)o后,把(Circle)o整体的静态类型变成了Circle。动态类型仍然是ThreeDCricle
((Circle)o).Draw();
// 执行ThreeDCricle::draw
((Circle)o).Draw(10);
}
}
4.基类/派生类的转换规则
如果两个类通过is-a关系关联,在基类引用中保存派生类型总是安全的。
使用C#强制转换 操作符进行显式的向下转换。
5.C#的as
强制转换在运行时,如果转换失败,会收到运行时异常。
使用as,就可以通过检查null返回值来检测兼容性
// frank可以转换为Hexagon类型时,返回转换后的Hexagon类型。不能时,返回null
Hexagon hex2 = frank as Hexagon;
if(hex2 == null)
…
6.C#的is
如果类型不兼容,is关键字就返回false,而不是null引用
static void GivePromotion(Employee emp)
{
if(emp is Manager)
{
((Manager)emp).xxx
}
}
7.System.Object
查看.NET开源实现的地址:
https://referencesource.microsoft.com/