C#继承与多态


一、继承的基本概念

继承是面向对象编程的三大特性之一(封装、继承、多态),它允许我们基于现有类创建新类,实现代码的重用和扩展。

1. 基本语法

// 基类(父类)
public class Animal
{
    public string Name { get; set; }

    public void Eat()
    {
        Console.WriteLine($"{Name}在吃东西");
    }
}

// 派生类(子类)
public class Dog : Animal  // 使用冒号表示继承
{
    public void Bark()
    {
        Console.WriteLine($"{Name}在汪汪叫");
    }
}

2. 继承的特点

  • 子类继承父类的所有公共(public)和保护(protected)成员
  • 子类可以添加新成员(扩展)
  • 子类可以修改继承的成员(重写)
  • C#只支持单继承(一个类只能直接继承一个父类)

二、继承的使用

1. 基本使用示例

Dog myDog = new Dog();
myDog.Name = "阿黄";  // 继承自Animal的属性
myDog.Eat();        // 继承自Animal的方法
myDog.Bark();       // Dog类自身的方法

2. 构造函数继承

子类构造函数可以通过base关键字调用父类构造函数:

public class Vehicle
{
    public string Brand { get; }

    public Vehicle(string brand)
    {
        Brand = brand;
    }
}

public class Car : Vehicle
{
    public int Wheels { get; }

    public Car(string brand, int wheels) : base(brand)
    {
        Wheels = wheels;
    }
}

// 使用
Car myCar = new Car("丰田", 4);
Console.WriteLine($"品牌:{myCar.Brand},轮子数:{myCar.Wheels}");

三、多态的实现

多态是指同一操作作用于不同对象时,可以有不同的解释和执行结果。C#主要通过以下方式实现多态:

1. 虚方法(virtual)和重写(override)

public class Shape
{
    public virtual void Draw()  // 虚方法
    {
        Console.WriteLine("绘制基本形状");
    }
}

public class Circle : Shape
{
    public override void Draw()  // 重写方法
    {
        Console.WriteLine("绘制圆形");
    }
}

public class Rectangle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("绘制矩形");
    }
}

// 使用多态
Shape[] shapes = new Shape[3];
shapes[0] = new Shape();
shapes[1] = new Circle();
shapes[2] = new Rectangle();

foreach (Shape shape in shapes)
{
    shape.Draw();  // 根据实际对象类型调用相应方法
}

2. 抽象类(abstract)和抽象方法

public abstract class Animal
{
    public abstract void MakeSound();  // 抽象方法,无实现

    public void Sleep()
    {
        Console.WriteLine("动物在睡觉");
    }
}

public class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("喵喵叫");
    }
}

public class Duck : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("嘎嘎叫");
    }
}

// 使用
Animal myCat = new Cat();
Animal myDuck = new Duck();

myCat.MakeSound();  // 输出"喵喵叫"
myDuck.MakeSound(); // 输出"嘎嘎叫"

3. 隐藏方法(new)

当不希望重写父类方法而是完全隐藏它时,可以使用new关键字:

public class BaseClass
{
    public void Show()
    {
        Console.WriteLine("基类方法");
    }
}

public class DerivedClass : BaseClass
{
    public new void Show()
    {
        Console.WriteLine("派生类方法");
    }
}

// 使用
BaseClass obj1 = new DerivedClass();
obj1.Show();  // 输出"基类方法"

DerivedClass obj2 = new DerivedClass();
obj2.Show();  // 输出"派生类方法"

四、密封类(sealed)和密封方法

1. 密封类

阻止其他类继承该类:

public sealed class FinalClass
{
    // 类成员
}

// 以下代码会报错
// public class TryDerive : FinalClass {}

2. 密封方法

在重写方法上使用sealed,阻止派生类进一步重写该方法:

public class A
{
    public virtual void Method() {}
}

public class B : A
{
    public sealed override void Method() {}
}

public class C : B
{
    // 以下代码会报错
    // public override void Method() {}
}

五、is和as运算符

1. is运算符

检查对象是否与给定类型兼容:

Animal animal = new Cat();

if (animal is Cat)
{
    Console.WriteLine("这是一只猫");
}

2. as运算符

尝试将对象转换为指定类型,失败则返回null:

Animal animal = new Duck();
Duck duck = animal as Duck;

if (duck != null)
{
    duck.MakeSound();
}

六、综合应用示例

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        // 创建员工集合
        List<Employee> employees = new List<Employee>
        {
            new Manager("张经理", 15000, 5000),
            new Developer("李开发", 12000, "C#"),
            new SalesPerson("王销售", 8000, 0.1)
        };

        // 多态调用
        foreach (var emp in employees)
        {
            emp.DisplayInfo();
            Console.WriteLine($"月收入:{emp.CalculatePay()}\n");
        }
    }
}

// 基类
public abstract class Employee
{
    public string Name { get; }
    public decimal BaseSalary { get; }

    protected Employee(string name, decimal baseSalary)
    {
        Name = name;
        BaseSalary = baseSalary;
    }

    public abstract decimal CalculatePay();

    public virtual void DisplayInfo()
    {
        Console.WriteLine($"员工姓名:{Name}");
        Console.WriteLine($"基本工资:{BaseSalary}");
    }
}

// 派生类:经理
public class Manager : Employee
{
    public decimal Bonus { get; }

    public Manager(string name, decimal baseSalary, decimal bonus) 
        : base(name, baseSalary)
    {
        Bonus = bonus;
    }

    public override decimal CalculatePay()
    {
        return BaseSalary + Bonus;
    }

    public override void DisplayInfo()
    {
        base.DisplayInfo();
        Console.WriteLine($"职位:经理,奖金:{Bonus}");
    }
}

// 派生类:开发人员
public class Developer : Employee
{
    public string Language { get; }

    public Developer(string name, decimal baseSalary, string language) 
        : base(name, baseSalary)
    {
        Language = language;
    }

    public override decimal CalculatePay()
    {
        return BaseSalary * 1.1m;  // 开发人员有10%的技术津贴
    }

    public override void DisplayInfo()
    {
        base.DisplayInfo();
        Console.WriteLine($"职位:开发人员,主攻语言:{Language}");
    }
}

// 派生类:销售人员
public class SalesPerson : Employee
{
    public double CommissionRate { get; }

    public SalesPerson(string name, decimal baseSalary, double rate) 
        : base(name, baseSalary)
    {
        CommissionRate = rate;
    }

    public override decimal CalculatePay()
    {
        // 假设销售额为100000来计算佣金
        decimal commission = 100000 * (decimal)CommissionRate;
        return BaseSalary + commission;
    }

    public override void DisplayInfo()
    {
        base.DisplayInfo();
        Console.WriteLine($"职位:销售人员,佣金比例:{CommissionRate:P0}");
    }
}

七、继承与多态的最佳实践

  1. 合理使用继承:只有在”是一个”关系成立时才使用继承
  2. 优先使用组合:当”有一个”关系更合适时,使用组合而非继承
  3. 遵循LSP原则:里氏替换原则,子类应该能够替换父类
  4. 慎用方法隐藏:使用new关键字隐藏方法通常不是好的设计
  5. 合理设计抽象:将通用行为放在基类,变化行为通过虚方法或抽象方法实现
  6. 避免过深继承:继承层次不宜过深,通常不超过3-4层

继承与多态是C#面向对象编程的核心概念,合理运用这些特性可以创建出灵活、可扩展的应用程序架构。通过继承实现代码重用,通过多态实现接口统一而行为多样,这是构建复杂系统的有力工具。


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注