编译时多态与运行时多态
本课属于 Coddy C# 学习旅程的“面向对象编程”单元。第 25 课,共 70 课。
多态意味着“多种形式”,也就是说,同一段代码可以根据上下文表现出不同的行为。C# 支持两种不同的多态,了解它们各自在何时确定,有助于你编写更灵活的代码。
编译时多态(也称为静态多态)由编译器在程序运行前解析。方法重载是最主要的例子:多个方法使用相同的名称,但参数不同:
public class Calculator
{
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
}
var calc = new Calculator();
calc.Add(5, 3); // 编译器选择 int 版本
calc.Add(5.0, 3.0); // 编译器选择 double 版本编译器会检查参数类型,并在编译时选择正确的方法。程序运行之前,这个决定就已确定。
运行时多态(动态多态)在程序运行时确定。这会通过 virtual 和 override 方法实现,实际的对象类型决定执行哪个方法:
Animal pet = new Dog(); // 声明为 Animal,实际上是 Dog
pet.Speak(); // 运行时调用 Dog 的 Speak()编译器不知道会运行哪个 Speak(),这个决定会在运行时根据对象的实际类型作出。这就是基于继承的多态在构建灵活、可扩展的系统时如此强大的原因。
挑战
简单我们来构建一个转换器系统,展示两种多态的实际效果。你将创建一个使用方法重载(编译时多态)的类,以及一个使用 virtual/override 方法的继承层次结构(运行时多态)。
你将把代码分放在 4 个文件中:
Converter.cs:在Conversion命名空间中定义一个Converter类。这个类通过方法重载展示编译时多态。创建 3 个重载的Convert方法:Convert(int value)返回"Integer: {value}"Convert(double value)返回"Double: {value}"Convert(string value)返回"String: {value}"
Shape.cs:在Conversion命名空间中定义一个基类Shape。这个类有一个Name属性(字符串),以及一个用于设置该属性的构造函数。添加一个名为Describe()的virtual方法,返回"This is a {Name}"。Circle.cs:在Conversion命名空间中定义一个继承自Shape的Circle类。添加一个Radius属性(double)。构造函数接收一个半径,并将"Circle"传给基类构造函数。重写Describe(),使其返回"This is a Circle with radius {Radius}"。Program.cs:在主文件中展示两种多态。首先,创建一个Converter,并使用输入值调用全部 3 个重载方法。然后创建一个Circle,将它存储在一个Shape变量中,并调用Describe()来展示运行时多态。
你将收到 4 个输入:
- 一个整数值
- 一个 double 值
- 一个字符串值
- 一个圆的半径(double)
按以下格式打印输出:
Compile-time Polymorphism:
{Convert(int) result}
{Convert(double) result}
{Convert(string) result}
Runtime Polymorphism:
{Describe() result from Shape variable holding Circle}例如,如果输入是 42、3.14、Hello 和 5.5,输出应为:
Compile-time Polymorphism:
Integer: 42
Double: 3.14
String: Hello
Runtime Polymorphism:
This is a Circle with radius 5.5注意这个关键区别:编译器会在编译时根据参数类型决定调用哪个 Convert 方法,而 Describe() 方法会在运行时根据实际对象类型确定,即使它存储在一个 Shape 变量中也是如此!
亲手试试
using System;
using Conversion;
class Program
{
public static void Main(string[] args)
{
// 读取输入
int intValue = Convert.ToInt32(Console.ReadLine());
double doubleValue = Convert.ToDouble(Console.ReadLine());
string stringValue = Console.ReadLine();
double radius = Convert.ToDouble(Console.ReadLine());
// TODO:演示编译时多态
// 创建一个 Converter 对象,并调用全部三个重载的 Convert 方法
Console.WriteLine("Compile-time Polymorphism:");
// 分别用 intValue、doubleValue 和 stringValue 调用 Convert
// TODO:演示运行时多态
// 创建一个 Circle 对象,并将它存入 Shape 变量
// 然后通过 Shape 变量调用 Describe()
Console.WriteLine("Runtime Polymorphism:");
// 创建 Circle 对象并调用 Describe()
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C# 在线编译器