精通C#学习笔记---继承和多态

继承的基本机制

代码重用归为两类:继承(is-a),包含/委托(has-a)
继承保护了封装。

is-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类型成员,可以在所属类和其派生类内部实现被使用,也可被类或其派生类对象通过点操作符访问。

has-a

包含/委托编程

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/

你可能感兴趣的:(Language-c#)