C# 基础——多态的实现方式
在C#中,多态(Polymorphism)是面向对象编程的核心特性之一,指“同一操作作用于不同对象时,可产生不同的执行结果”。其本质是通过统一的接口(或基类)调用不同实现,实现代码的灵活性和可扩展性。
C#中多态的实现方式主要有以下4种,核心是基于动态绑定(运行时确定具体调用的方法) 或静态绑定(编译时确定):
1. 继承+虚方法重写(virtual + override)
这是最常用的动态多态实现方式,通过基类定义虚方法,派生类重写该方法,最终通过基类引用调用时,会自动执行派生类的实现。
实现步骤:
- 基类中用
virtual
关键字声明方法(含默认实现); - 派生类中用
override
关键字重写该方法(提供自定义实现); - 通过基类引用指向派生类对象,调用方法时自动执行派生类的重写版本。
示例代码:
// 基类
public class Animal {// 虚方法:定义统一接口,提供默认实现public virtual void Speak() {Console.WriteLine("动物发出声音");}
}// 派生类1:重写虚方法
public class Dog : Animal {public override void Speak() {Console.WriteLine("狗叫:汪汪");}
}// 派生类2:重写虚方法
public class Cat : Animal {public override void Speak() {Console.WriteLine("猫叫:喵喵");}
}// 调用:多态体现
class Program {static void Main() {Animal animal1 = new Dog(); // 基类引用指向派生类对象Animal animal2 = new Cat();animal1.Speak(); // 输出:狗叫:汪汪(执行Dog的实现)animal2.Speak(); // 输出:猫叫:喵喵(执行Cat的实现)}
}
特点:
- 动态绑定:运行时根据对象实际类型确定调用的方法;
- 基类有默认实现,派生类可选择是否重写(不重写则沿用基类逻辑)。
2. 抽象类+抽象方法(abstract)
抽象类是无法实例化的基类,其中的抽象方法(abstract
)没有默认实现,必须由派生类强制重写。通过抽象类引用调用方法时,同样会执行派生类的实现,属于动态多态。
实现步骤:
- 用
abstract
关键字定义抽象类; - 抽象类中用
abstract
声明抽象方法(只有签名,无方法体); - 派生类必须用
override
重写所有抽象方法; - 通过抽象类引用指向派生类对象,调用方法时执行派生类实现。
示例代码:
// 抽象基类(无法实例化)
public abstract class Shape {// 抽象方法:必须由派生类实现public abstract double GetArea();
}// 派生类1:实现抽象方法
public class Circle : Shape {public double Radius { get; set; }public Circle(double radius) => Radius = radius;public override double GetArea() {return Math.PI * Radius * Radius; // 圆面积公式}
}// 派生类2:实现抽象方法
public class Rectangle : Shape {public double Width { get; set; }public double Height { get; set; }public Rectangle(double w, double h) {Width = w;Height = h;}public override double GetArea() {return Width * Height; // 矩形面积公式}
}// 调用:多态体现
class Program {static void Main() {Shape shape1 = new Circle(2); // 抽象类引用指向派生类Shape shape2 = new Rectangle(3, 4);Console.WriteLine(shape1.GetArea()); // 输出:12.566...(圆面积)Console.WriteLine(shape2.GetArea()); // 输出:12(矩形面积)}
}
特点:
- 动态绑定:运行时确定调用的方法;
- 强制派生类实现抽象方法(避免“忘记重写”的错误);
- 抽象类可包含非抽象方法(提供默认实现)。
3. 接口实现(interface)
接口是一种“行为契约”,仅定义方法签名(无实现),类通过 implements
关键字实现接口时,必须实现接口的所有方法。不同类可实现同一接口的不同逻辑,通过接口引用调用时体现多态。
实现步骤:
- 用
interface
关键字定义接口(方法无访问修饰符,默认public); - 类用
: 接口名
实现接口,并实现所有接口方法; - 通过接口引用指向实现类对象,调用方法时执行对应类的实现。
示例代码:
// 接口:定义行为契约
public interface IFlyable {void Fly(); // 仅声明,无实现
}// 实现类1:鸟
public class Bird : IFlyable {public void Fly() {Console.WriteLine("鸟扇动翅膀飞行");}
}// 实现类2:飞机
public class Plane : IFlyable {public void Fly() {Console.WriteLine("飞机靠引擎飞行");}
}// 调用:多态体现
class Program {static void Main() {IFlyable flyer1 = new Bird(); // 接口引用指向实现类IFlyable flyer2 = new Plane();flyer1.Fly(); // 输出:鸟扇动翅膀飞行flyer2.Fly(); // 输出:飞机靠引擎飞行}
}
特点:
- 动态绑定:运行时确定调用的方法;
- 接口支持“多实现”(一个类可实现多个接口),而类只能单继承,因此接口更灵活;
- 适合定义跨类别的通用行为(如“飞行”可被鸟、飞机等不同类型的类实现)。
4. 隐藏基类方法(new关键字)
通过 new
关键字在派生类中定义与基类同名的方法,会“隐藏”基类方法(而非重写)。这种方式属于静态多态(编译时绑定),调用哪个版本的方法由“引用类型”而非“对象实际类型”决定。
实现步骤:
- 基类定义普通方法;
- 派生类用
new
关键字定义同名方法(隐藏基类方法); - 调用时,基类引用调用基类方法,派生类引用调用派生类方法。
示例代码:
public class Parent {public void Print() {Console.WriteLine("父类方法");}
}public class Child : Parent {// new关键字:隐藏父类的Print方法public new void Print() {Console.WriteLine("子类方法");}
}class Program {static void Main() {Parent p = new Child(); // 基类引用指向派生类对象Child c = new Child(); // 派生类引用p.Print(); // 输出:父类方法(由引用类型Parent决定)c.Print(); // 输出:子类方法(由引用类型Child决定)}
}
注意:
- 这不是“真正的多态”,因为行为由引用类型(编译时已知)决定,而非对象实际类型;
- 通常不推荐使用,容易导致逻辑混淆(建议优先用
override
重写)。
总结:多态实现方式的对比
方式 | 核心关键字 | 绑定时机 | 适用场景 |
---|---|---|---|
虚方法重写 | virtual + override | 运行时 | 基类有默认实现,派生类可选择性重写 |
抽象类+抽象方法 | abstract + override | 运行时 | 基类无默认实现,强制派生类提供具体逻辑 |
接口实现 | interface | 运行时 | 跨类别的行为约定,支持多实现 |
隐藏基类方法 | new | 编译时 | 特殊场景(不推荐,易混淆) |
实际开发中,接口实现和虚方法重写是最常用的多态方式,它们通过动态绑定实现了“统一接口,不同实现”,大幅提升了代码的扩展性和可维护性。