Administrator
发布于 2025-10-14 / 14 阅读
0
0

多态是什么

一、多态是什么?

多态:一个方法,多种表现。同一操作作用于不同的对象,可以产生不的行为。

  • 同一操作:相同的方法调用、相同的操作符、相同的接口

  • 不同对象:同一类型的不同子类或不同类型的对象。

  • 不同行为:根据对象的实际类型执行不同的行为

其他定义:

  • 多态: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();  // 同一操作,不同行为
        //}
    }
}

三、实现多态的方式

多态类型

实现方式

举例关键字/特征

多态发生时间

✅ 1. 虚方法重写

继承 + virtual/override

virtualoverride

运行时(动态)

✅ 2. 接口实现

接口 + interface

interfaceimplements

运行时(动态)

✅ 3. 方法重载

同名不同参数

Overloading

编译时(静态)

🔹 4. 抽象类(Abstract Class)

抽象方法 + abstract/override

abstract

运行时(动态)

🔹 5. 方法隐藏(Method Hiding)

new 关键字

new

编译时(静态)

🔹 6. 泛型(Generics)

类型参数多态

<T>

编译时(静态)

🔹 7. 委托与事件(Delegate/Event)

通过回调函数行为多态

delegateevent

运行时(动态)

🔹 8. 模式匹配(Pattern Matching)

isswitchwhen

C# 7+ 新特性

运行时(动态)

总结对比表(更全面)

分类

示例机制

多态类型

时间

特点

1

virtual / override

继承

运行时

最经典的实现方式

2

interface 接口

实现

运行时

跨类层次结构

3

方法重载

参数多态

编译时

静态绑定

4

抽象类

抽象方法

运行时

强制子类实现

5

new 隐藏

方法遮蔽

编译时

不是严格多态

6

泛型

类型参数

编译时

一种模板多态

7

委托 / 事件

回调多态

运行时

行为多态

8

模式匹配

类型检测

运行时

C# 7+ 动态多态

四、“虚方法重写”实现多态介绍

“虚方法重写”是指通过一套设置关键字的规则实现多态。具体规则如下:

  • 使用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();  // 输出:喵喵喵
}

六、多态的好处

  1. 代码更灵活:可以在不知道具体类名的情况下调用正确的方法。

  2. 更容易扩展:添加新类(比如 Bird)只需要继承 Animal 并实现自己的 Speak() 方法即可。

  3. 便于维护:可以用一个统一的接口或基类来处理各种不同的对象。

七、从继承到多态的演进

阶段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:交互式员工管理系统

虚方法重写练习,具有以下功能:

  1. 添加全职员工 - 输入姓名和月薪

  2. 添加兼职员工 - 输入姓名、工作小时和时薪

  3. 查看所有员工信息 - 显示所有已添加员工的基本信息

  4. 计算所有员工工资 - 计算并显示每个员工的工资及总工资

  5. 演示多态性 - 展示如何使用基类引用调用不同派生类的方法

  6. 退出系统

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


评论