跳到主要内容

C# 多态

本章将深入介绍 C# 中的多态(Polymorphism)特性,包括编译时多态和运行时多态。

多态概述

什么是多态?

多态是指同一操作作用于不同的对象,可以产生不同的行为。在 C# 中,多态主要通过以下方式实现:

  1. 编译时多态(静态多态):方法重载
  2. 运行时多态(动态多态):方法重写(override)
┌─────────────────────────────────────────┐
│ 多态 │
├─────────────────────────────────────────┤
│ 编译时多态 运行时多态 │
│ (方法重载) (方法重写) │
│ - 方法名相同 - 基类 virtual │
│ - 参数不同 - 派生类 override │
└─────────────────────────────────────────┘

编译时多态:方法重载

方法重载

class Calculator
{
// 相同方法名,不同参数
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
public int Add(int a, int b, int c) => a + b + c;
public string Add(string a, string b) => a + b;
}

Calculator calc = new Calculator();
calc.Add(1, 2); // int 版本
calc.Add(1.5, 2.5); // double 版本
calc.Add(1, 2, 3); // 三个参数版本
calc.Add("Hello", "World"); // 字符串版本

构造函数重载

class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string City { get; set; }

// 无参构造函数
public Person()
{
Name = "未知";
Age = 0;
}

// 带参构造函数
public Person(string name) : this(name, 0)
{
}

// 完整构造函数
public Person(string name, int age)
{
Name = name;
Age = age;
}

// 另一个重载
public Person(string name, int age, string city)
{
Name = name;
Age = age;
City = city;
}
}

// 使用
Person p1 = new Person();
Person p2 = new Person("张三");
Person p3 = new Person("李四", 20);
Person p4 = new Person("王五", 25, "北京");

运算符重载

struct Complex
{
public double Real { get; set; }
public double Imag { get; set; }

public Complex(double real, double imag)
{
Real = real;
Imag = imag;
}

// 重载 + 运算符
public static Complex operator +(Complex a, Complex b)
=> new Complex(a.Real + b.Real, a.Imag + b.Imag);

// 重载 - 运算符
public static Complex operator -(Complex a, Complex b)
=> new Complex(a.Real - b.Real, a.Imag - b.Imag);

// 重载 * 运算符
public static Complex operator *(Complex a, Complex b)
=> new Complex(
a.Real * b.Real - a.Imag * b.Imag,
a.Real * b.Imag + a.Imag * b.Real
);

public override string ToString() => $"{Real}+{Imag}i";
}

// 使用
Complex c1 = new Complex(1, 2);
Complex c2 = new Complex(3, 4);
Complex c3 = c1 + c2;
Complex c4 = c1 * c2;

运行时多态:方法重写

虚方法和重写

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

public virtual double GetArea()
{
return 0;
}
}

// 派生类
class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }

public Rectangle(double width, double height)
{
Width = width;
Height = height;
}

// override 重写基类方法
public override void Draw()
{
Console.WriteLine($"绘制矩形: {Width}x{Height}");
}

public override double GetArea() => Width * Height;
}

class Circle : Shape
{
public double Radius { get; set; }

public Circle(double radius)
{
Radius = radius;
}

public override void Draw()
{
Console.WriteLine($"绘制圆: 半径={Radius}");
}

public override double GetArea() => Math.PI * Radius * Radius;
}

多态调用

// 创建不同类型的对象
Shape[] shapes = {
new Rectangle(5, 3),
new Circle(4),
new Shape()
};

// 遍历调用(多态)
foreach (var shape in shapes)
{
shape.Draw(); // 调用各自的重写方法
Console.WriteLine($"面积: {shape.GetArea()}");
}

/*
输出:
绘制矩形: 5x3
面积: 15
绘制圆: 半径=4
面积: 50.265...
绘制形状
面积: 0
*/

基类引用指向派生类对象

// 基类引用可以指向任何派生类对象
Shape s1 = new Rectangle(10, 5);
Shape s2 = new Circle(3);
Shape s3 = new Shape();

// 运行时决定调用哪个方法
s1.Draw(); // 绘制矩形
s2.Draw(); // 绘制圆
s3.Draw(); // 基类方法

抽象类与多态

// 抽象类
abstract class Animal
{
public string Name { get; set; }

// 抽象方法(强制派生类实现)
public abstract void Speak();

// 具体方法(可以重写)
public virtual void Move()
{
Console.WriteLine($"{Name} 在移动");
}
}

class Dog : Animal
{
public override void Speak()
{
Console.WriteLine($"{Name}: 汪汪!");
}

public override void Move()
{
Console.WriteLine($"{Name} 跑来了");
}
}

class Cat : Animal
{
public override void Speak()
{
Console.WriteLine($"{Name}: 喵喵~");
}

public new void Move() // 使用 new 隐藏
{
Console.WriteLine($"{Name} 轻盈地走来了");
}
}

// 多态调用
Animal[] animals = {
new Dog { Name = "旺财" },
new Cat { Name = "咪咪" }
};

foreach (var animal in animals)
{
animal.Speak(); // 多态
animal.Move(); // 多态(virtual)
}

接口与多态

// 定义接口
interface IPayment
{
void Process(decimal amount);
bool Refund(decimal amount);
}

class Alipay : IPayment
{
public void Process(decimal amount)
{
Console.WriteLine($"支付宝支付: {amount}元");
}

public bool Refund(decimal amount)
{
Console.WriteLine($"支付宝退款: {amount}元");
return true;
}
}

class WeChatPay : IPayment
{
public void Process(decimal amount)
{
Console.WriteLine($"微信支付: {amount}元");
}

public bool Refund(decimal amount)
{
Console.WriteLine($"微信退款: {amount}元");
return true;
}
}

// 多态使用
IPayment[] payments = { new Alipay(), new WeChatPay() };
foreach (var payment in payments)
{
payment.Process(100);
}

/*
输出:
支付宝支付: 100元
微信支付: 100元
*/

sealed 关键字

密封类

// 密封类:不能被继承
sealed class MathHelper
{
public static int Max(int a, int b) => a > b ? a : b;
}

// 错误!不能继承密封类
// class AdvancedMath : MathHelper { }

密封方法

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

class DerivedClass : BaseClass
{
// 密封方法:阻止进一步重写
public sealed override void Method()
{
Console.WriteLine("DerivedClass.Method");
}
}

class FurtherDerived : DerivedClass
{
// 错误!不能重写已密封的方法
// public override void Method() { }
}

is 和 as 运算符

类型检查

object obj = new Rectangle(5, 3);

// is: 检查类型
if (obj is Rectangle)
{
Console.WriteLine("是矩形");
}

// is with pattern (C# 7+)
if (obj is Rectangle rect)
{
Console.WriteLine($"矩形面积: {rect.GetArea()}");
}

// as: 安全转换
Rectangle? rect2 = obj as Rectangle;
if (rect2 != null)
{
Console.WriteLine($"矩形面积: {rect2.GetArea()}");
}

泛型与多态

interface IRepository<T>
{
T GetById(int id);
IEnumerable<T> GetAll();
void Add(T entity);
}

class Repository<T> : IRepository<T> where T : class
{
public virtual T GetById(int id)
{
// 获取逻辑
return null;
}

public virtual IEnumerable<T> GetAll()
{
return new List<T>();
}

public virtual void Add(T entity)
{
// 添加逻辑
}
}

工厂模式与多态

// 抽象产品
interface IProduct
{
void Operation();
}

// 具体产品
class ProductA : IProduct
{
public void Operation() => Console.WriteLine("产品A操作");
}

class ProductB : IProduct
{
public void Operation() => Console.WriteLine("产品B操作");
}

// 工厂
class ProductFactory
{
public static IProduct Create(string type)
{
return type switch
{
"A" => new ProductA(),
"B" => new ProductB(),
_ => throw new ArgumentException("未知类型")
};
}
}

// 使用
IProduct product = ProductFactory.Create("A");
product.Operation();

小结

  1. 编译时多态:通过方法重载、运算符重载实现
  2. 运行时多态:通过虚方法、抽象方法、接口实现
  3. sealed:阻止进一步继承或重写
  4. is/as:类型检查和安全转换
  5. 模式匹配:C# 7+ 的类型检查语法

练习

  1. 创建一个图形系统,支持不同图形的面积计算
  2. 使用接口实现支付方式的多态处理
  3. 设计一个通知系统,支持多种通知方式(邮件、短信、推送)
  4. 使用工厂模式创建不同类型的对象