C# 多态性

静态多态(重载)与动态多态(虚方法、接口),以及 `is`/`as` 转型。

Intermediate
Key Points多态 = 同一个调用,根据实际类型执行不同的实现。

两种多态

KindMechanismDecided
静态多态方法重载、泛型编译期
动态多态虚方法、接口运行期

动态多态

C#
abstract class Payment
{
    public abstract string Pay(decimal amount);
}

class Alipay : Payment
{
    public override string Pay(decimal amount) => $"支付宝支付 {amount:C}";
}

class Wechat : Payment
{
    public override string Pay(decimal amount) => $"微信支付 {amount:C}";
}

class Card : Payment
{
    public override string Pay(decimal amount) => $"银行卡支付 {amount:C}";
}

// 调用方只依赖抽象,不关心具体实现
void Checkout(Payment p, decimal amount)
{
    Console.WriteLine(p.Pay(amount));
}

Checkout(new Alipay(), 99.5m);
Checkout(new Wechat(), 99.5m);
Checkout(new Card(), 99.5m);
Output
支付宝支付 ¥99.50
微信支付 ¥99.50
银行卡支付 ¥99.50

Checkout never changed yet supports three payment methods. That is the value of polymorphism: adding an implementation needs no change to the caller — the open/closed principle.

接口多态

C#
interface ILogger
{
    void Log(string msg);
}

class ConsoleLogger : ILogger
{
    public void Log(string msg) => Console.WriteLine($"[控制台] {msg}");
}

class FileLogger : ILogger
{
    public void Log(string msg) => Console.WriteLine($"[文件] {msg}");
}

class Service
{
    private readonly ILogger _log;
    public Service(ILogger log) => _log = log;      // 构造注入

    public void Run() => _log.Log("服务启动");
}

new Service(new ConsoleLogger()).Run();
new Service(new FileLogger()).Run();
Output
[控制台] 服务启动
[文件] 服务启动

That is dependency injection: the class depends on an interface, implementations arrive from outside. In tests you pass a fake ILogger and assert how often it was called.

转型

C#
Payment p = new Alipay();

// is:判断
Console.WriteLine(p is Alipay);
Console.WriteLine(p is Wechat);

// is 带模式变量:判断并转型
if (p is Alipay a)
{
    Console.WriteLine($"确实是支付宝:{a.GetType().Name}");
}

// as:转型,失败返回 null
var w = p as Wechat;
Console.WriteLine(w == null);

// 强转:失败抛异常
try
{
    var bad = (Wechat)p;
}
catch (InvalidCastException)
{
    Console.WriteLine("强转失败");
}

// 类型判断的 switch
string kind = p switch
{
    Alipay => "支付宝",
    Wechat => "微信",
    Card => "银行卡",
    _ => "其他"
};
Console.WriteLine(kind);
Output
True
False
确实是支付宝:Alipay
True
强转失败
支付宝

重载:静态多态

C#
class Formatter
{
    public string Format(int x) => $"整数 {x}";
    public string Format(double x) => $"浮点 {x:F2}";
    public string Format(string x) => $"字符串 {x}";
    public string Format(int x, int width) => $"补零 {x.ToString($"D{width}")}";
}

var f = new Formatter();
Console.WriteLine(f.Format(42));
Console.WriteLine(f.Format(3.14159));
Console.WriteLine(f.Format("abc"));
Console.WriteLine(f.Format(42, 5));
Output
整数 42
浮点 3.14
字符串 abc
补零 00042
Overload resolution happens at compile time, so object o = 42; f.Format(o) fails — the compiler sees object with no matching overload. Only dynamic defers to runtime.

多态的三个条件

  1. 父类/接口声明(Payment / ILogger)
  2. 子类重写实现(override / 实现接口)
  3. 用父类/接口类型的引用调用(Payment p = new Alipay())

The third is the one people miss: Alipay a = new Alipay(); a.Pay(...) is statically bound — no polymorphism. The variable must be declared as the base type for runtime dispatch.

Friends & Partners