一、多态是什么?
多态:一个方法,多种表现。同一操作作用于不同的对象,可以产生不的行为。
同一操作:相同的方法调用、相同的操作符、相同的接口
不同对象:同一类型的不同子类或不同类型的对象。
不同行为:根据对象的实际类型执行不同的行为
其他定义:
多态:Polymorphism(希腊语),意思是“多种形态”。
多态是类的三大特性之一。(另外两个是封装和继承)
多态是面向对象程序设计的重要特征。
二、多态的样子
using System;
public class Program
{
public class Animal
{
public abstract void Speak(); // 同一操作
}
public class Dog : Animal
{
public override void Speak() // 不同行为
{
Console.WriteLine("汪汪!");
}
}
public class Cat : Animal
{
public override void Speak() // 不同行为
{
Console.WriteLine("喵喵!");
}
}
public static void Main()
{
// 同一操作:MakeSound()
// 不同对象:Dog, Cat
// 不同行为:汪汪、喵喵
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.Speak();
a2.Speak();
//Animal[] animals = { new Dog(), new Cat(), new Bird() };
//foreach (Animal animal in animals)
//{
// animal.MakeSound(); // 同一操作,不同行为
//}
}
}三、实现多态的方式
总结对比表(更全面)
四、“虚方法重写”实现多态介绍
“虚方法重写”是指通过一套设置关键字的规则实现多态。具体规则如下:
使用virtual关键字在基类中标记可以重写的方法,表示该方法可以被派生类重写。
使用override关键字在派生类中标记要重写的基类方法,表示该方法是重写基类的虚方法。
virtual 关键字写在基类的方法中
public class Animal // 基类
{
// virtual 表示该方法可以被派生类重写。
public virtual void Speak()
{
Console.WriteLine("动物在叫");
}
}override 关键字**写在派生类的方法中
// 派生类1
public class Dog : Animal // 派生类
{
// override 表示该方法是重写基类的虚方法
public override void Speak()
{
Console.WriteLine("汪汪汪");
}
}
// 派生类2
public class Cat : Animal
{
// // override 表示该方法是重写基类的虚方法
public override void Speak()
{
Console.WriteLine("喵喵喵");
}
}创建同一操作的不同对象
public static void Main()
{
// 子类(派生类)对象可以赋值给父类(基类)变量。
// 这被称为“向上转型”(upcasting)。
// 这样做是安全的,
// 因为狗(Dog)是一个Animal,
// 猫(Cat)也是一个Animal。
// 所以可以用基类引用指向派生类对象
Animal a1 = new Dog();
Animal a2 = new Cat();
// 我们用的是同一个方法名:`Speak()`
// 但是根据对象的实际类型(`Dog` 或 `Cat`),程序调用了不同的实现。
// 编译时:只知道a1是Animal类型
// 运行时:发现a1实际上是Dog,所以调用Dog的Speak方法
// 说明了多态性:相同的方法调用(Speak())产生了不同的行为
// 具体执行哪个方法取决于对象的实际类型,而不是引用类型
a1.Speak(); // 输出:汪汪汪
a2.Speak(); // 输出:喵喵喵
}六、多态的好处
代码更灵活:可以在不知道具体类名的情况下调用正确的方法。
更容易扩展:添加新类(比如
Bird)只需要继承Animal并实现自己的Speak()方法即可。便于维护:可以用一个统一的接口或基类来处理各种不同的对象。
七、从继承到多态的演进
阶段1:普通继承(没有多态)
public class 动物
{
public void 叫()
{
Console.WriteLine("动物发出声音");
}
}
public class 狗 : 动物
{
// 隐藏父类方法(不好!)
public new void 叫()
{
Console.WriteLine("汪汪!");
}
}
public class 猫 : 动物
{
public new void 叫()
{
Console.WriteLine("喵喵!");
}
}
// 使用
狗 小黄 = new 狗();
猫 小花 = new 猫();
小黄.叫(); // 输出:汪汪!
小花.叫(); // 输出:喵喵!阶段2:使用多态(正确方式)
public class 动物
{
// virtual关键字:允许子类重写
public virtual void 叫()
{
Console.WriteLine("动物发出声音");
}
}
public class 狗 : 动物
{
// override关键字:重写父类方法
public override void 叫()
{
Console.WriteLine("汪汪!");
}
}
public class 猫 : 动物
{
public override void 叫()
{
Console.WriteLine("喵喵!");
}
}
// 使用多态的魅力!
动物 我的宠物1 = new 狗(); // 动物引用指向狗对象
动物 我的宠物2 = new 猫(); // 动物引用指向猫对象
我的宠物1.叫(); // 输出:汪汪!(调用狗的版本)
我的宠物2.叫(); // 输出:喵喵!(调用猫的版本)练习题
练习1:找错误
找错误1: 重写基类方法
public class BaseClass
{
public void ShowMessage() // 缺少 virtual
{
Console.WriteLine("基类消息");
}
}
public class DerivedClass : BaseClass
{
public override void ShowMessage() // 编译错误!
{
Console.WriteLine("派生类消息");
}
}找错误2: 重写基类方法
public class BaseClass
{
public virtual void ShowMessage()
{
Console.WriteLine("基类消息");
}
}
public class DerivedClass : BaseClass
{
public void ShowMessage() // 缺少 override,会隐藏而不是重写
{
Console.WriteLine("派生类消息");
}
}找错误3:重写基类方法
public class Animal
{
// 基类方法是public
public virtual void Speak() { }
}
public class Dog : Animal
{
// 派生类方法也必须是public
public override void Speak() { }
// ❌ 错误:不能改变访问级别
// private override void Speak() { }
}练习2:交互式员工管理系统
虚方法重写练习,具有以下功能:
添加全职员工 - 输入姓名和月薪
添加兼职员工 - 输入姓名、工作小时和时薪
查看所有员工信息 - 显示所有已添加员工的基本信息
计算所有员工工资 - 计算并显示每个员工的工资及总工资
演示多态性 - 展示如何使用基类引用调用不同派生类的方法
退出系统
using System;
namespace InteractiveEmployeeSystem
{
// 基类:员工
class Employee
{
public string Name { get; set; }
// 虚方法:计算工资
public virtual double CalculateSalary()
{
return 0;
}
// 虚方法:显示信息
public virtual void DisplayInfo()
{
Console.WriteLine($"员工: {Name}");
}
}
// 全职员工类
class FullTimeEmployee : Employee
{
public double MonthlySalary { get; set; }
public override double CalculateSalary()
{
return MonthlySalary;
}
public override void DisplayInfo()
{
Console.WriteLine($"全职员工: {Name}, 固定月薪: {MonthlySalary}元");
}
}
// 兼职员工类
class PartTimeEmployee : Employee
{
public int WorkHours { get; set; }
public double HourlyRate { get; set; }
public override double CalculateSalary()
{
return WorkHours * HourlyRate;
}
public override void DisplayInfo()
{
Console.WriteLine($"兼职员工: {Name}, 工作小时: {WorkHours}, 时薪: {HourlyRate}元");
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("=== 交互式员工管理系统 ===");
Console.WriteLine("练习虚方法重写和多态性");
Console.WriteLine();
bool running = true;
while (running)
{
Console.WriteLine("请选择操作:");
Console.WriteLine("1. 添加全职员工");
Console.WriteLine("2. 添加兼职员工");
Console.WriteLine("3. 查看所有员工信息");
Console.WriteLine("4. 计算所有员工工资");
Console.WriteLine("5. 演示多态性");
Console.WriteLine("6. 退出");
Console.Write("请输入选择 (1-6): ");
string choice = Console.ReadLine();
Console.WriteLine();
switch (choice)
{
case "1":
AddFullTimeEmployee();
break;
case "2":
AddPartTimeEmployee();
break;
case "3":
ShowAllEmployees();
break;
case "4":
CalculateAllSalaries();
break;
case "5":
DemonstratePolymorphism();
break;
case "6":
running = false;
Console.WriteLine("感谢使用员工管理系统,再见!");
break;
default:
Console.WriteLine("无效选择,请重新输入。");
break;
}
Console.WriteLine();
}
}
// 存储员工的数组
static Employee[] employees = new Employee[10];
static int employeeCount = 0;
// 添加全职员工
static void AddFullTimeEmployee()
{
if (employeeCount >= employees.Length)
{
Console.WriteLine("员工数量已达上限,无法添加新员工。");
return;
}
Console.Write("请输入员工姓名: ");
string name = Console.ReadLine();
Console.Write("请输入月薪: ");
if (double.TryParse(Console.ReadLine(), out double salary))
{
FullTimeEmployee emp = new FullTimeEmployee
{
Name = name,
MonthlySalary = salary
};
employees[employeeCount] = emp;
employeeCount++;
Console.WriteLine($"全职员工 {name} 添加成功!");
}
else
{
Console.WriteLine("输入的月薪无效,请重新操作。");
}
}
// 添加兼职员工
static void AddPartTimeEmployee()
{
if (employeeCount >= employees.Length)
{
Console.WriteLine("员工数量已达上限,无法添加新员工。");
return;
}
Console.Write("请输入员工姓名: ");
string name = Console.ReadLine();
Console.Write("请输入工作小时: ");
if (!int.TryParse(Console.ReadLine(), out int hours))
{
Console.WriteLine("输入的工作小时无效,请重新操作。");
return;
}
Console.Write("请输入时薪: ");
if (double.TryParse(Console.ReadLine(), out double rate))
{
PartTimeEmployee emp = new PartTimeEmployee
{
Name = name,
WorkHours = hours,
HourlyRate = rate
};
employees[employeeCount] = emp;
employeeCount++;
Console.WriteLine($"兼职员工 {name} 添加成功!");
}
else
{
Console.WriteLine("输入的时薪无效,请重新操作。");
}
}
// 显示所有员工信息
static void ShowAllEmployees()
{
if (employeeCount == 0)
{
Console.WriteLine("暂无员工信息。");
return;
}
Console.WriteLine("=== 所有员工信息 ===");
for (int i = 0; i < employeeCount; i++)
{
Console.Write($"{i+1}. ");
employees[i].DisplayInfo();
}
}
// 计算所有员工工资
static void CalculateAllSalaries()
{
if (employeeCount == 0)
{
Console.WriteLine("暂无员工信息。");
return;
}
Console.WriteLine("=== 员工工资计算 ===");
double totalSalary = 0;
for (int i = 0; i < employeeCount; i++)
{
double salary = employees[i].CalculateSalary();
Console.WriteLine($"{employees[i].Name} 的工资: {salary}元");
totalSalary += salary;
}
Console.WriteLine($"所有员工工资总额: {totalSalary}元");
}
// 演示多态性
static void DemonstratePolymorphism()
{
Console.WriteLine("=== 多态性演示 ===");
Console.WriteLine("创建不同类型的员工,但使用基类引用:");
// 使用基类引用指向不同类型的对象
Employee emp1 = new FullTimeEmployee { Name = "张三", MonthlySalary = 8000 };
Employee emp2 = new PartTimeEmployee { Name = "李四", WorkHours = 100, HourlyRate = 50 };
Console.WriteLine("使用相同的基类引用调用方法:");
Console.WriteLine();
Console.WriteLine("员工1:");
emp1.DisplayInfo();
Console.WriteLine($"计算工资: {emp1.CalculateSalary()}元");
Console.WriteLine();
Console.WriteLine("员工2:");
emp2.DisplayInfo();
Console.WriteLine($"计算工资: {emp2.CalculateSalary()}元");
Console.WriteLine();
Console.WriteLine("说明: 相同的调用方式(emp.DisplayInfo()和emp.CalculateSalary())");
Console.WriteLine("但根据对象的实际类型执行了不同的实现,这就是多态性!");
}
}
}c