在C#中,多态(Polymorphism)是面向对象编程的核心特性之一,指"同一操作作用于不同对象时,可产生不同的执行结果"。其本质是通过统一的接口(或基类)调用不同实现,实现代码的灵活性和可扩展性。
C#中多态的实现方式主要有以下4种,核心是基于动态绑定(运行时确定具体调用的方法) 或静态绑定(编译时确定):
1. 继承+虚方法重写(virtual + override)
这是最常用的动态多态实现方式,通过基类定义虚方法,派生类重写该方法,最终通过基类引用调用时,会自动执行派生类的实现。
实现步骤:
- 基类中用
virtual
关键字声明方法(含默认实现); - 派生类中用
override
关键字重写该方法(提供自定义实现); - 通过基类引用指向派生类对象,调用方法时自动执行派生类的重写版本。
示例代码:
csharp
// 基类
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
重写所有抽象方法; - 通过抽象类引用指向派生类对象,调用方法时执行派生类实现。
示例代码:
csharp
// 抽象基类(无法实例化)
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); - 类用
: 接口名
实现接口,并实现所有接口方法; - 通过接口引用指向实现类对象,调用方法时执行对应类的实现。
示例代码:
csharp
// 接口:定义行为契约
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
关键字定义同名方法(隐藏基类方法); - 调用时,基类引用调用基类方法,派生类引用调用派生类方法。
示例代码:
csharp
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 | 编译时 | 特殊场景(不推荐,易混淆) |
实际开发中,接口实现 和虚方法重写是最常用的多态方式,它们通过动态绑定实现了"统一接口,不同实现",大幅提升了代码的扩展性和可维护性。