C#中的多态性详解:编译时与运行时实现

半杯拿铁
半杯拿铁
发布时间:2026-10-13 | 4人浏览过
原创

C#作为强类型面向对象语言,其多态性是实现灵活代码架构的核心。多态主要分为两类:一是基于方法重载的编译时多态,二是基于虚方法重写的运行时多态。理解这两者的区别及关键字(如virtual、override、new)的使用场景,是编写高质量C#代码的基础。

编译时的多态性:方法重载

程序的执行通常经历编译、链接和运行三个阶段。编译时多态主要体现在方法重载(Overloading)上。这是指在同一个类中定义多个同名方法,但它们的参数列表(参数类型或个数)必须不同。

  • 机制:编译器在编译阶段根据调用时传入的参数类型和个数,确定具体绑定哪一个方法。
  • 特点:这是一种静态绑定,不涉及继承体系中的动态行为变化。

C#的方法重载机制与C++、Java类似,但C#额外支持运算符重载,这也是其特性之一。

运行时的多态性:虚方法与重写

运行时多态是指在程序运行过程中,当使用基类引用变量指向派生类对象时,调用同名方法能够执行派生类中定义的特定行为。这在C#中主要通过虚方法(Virtual Methods)实现。

第1步:声明基类的虚方法与非虚方法

要启用运行时多态,需要在基类中将希望被子类重写的方法标记为virtual。未标记的方法默认为非虚方法,即使子类中有同名方法,也不会发生多态调用。

namespace e_11 
{
    class A
    {
        public void F() // 非虚方法:固定绑定到A类
        { Console.WriteLine("A.F"); }

        public virtual void G() // 虚方法:允许后续重写
        { Console.WriteLine("A.G"); }
    }
}

第2步:在派生类中隐藏或重写方法

派生类对基类方法的处理有两种方式,需严格区分:

  1. 隐藏非虚方法(new):如果基类方法是普通的非虚方法,子类可以使用new关键字定义同名方法来“隐藏”它。这不会产生多态效果。
  2. 重写虚方法(override):如果基类方法标记为virtual,子类必须使用override关键字来提供新的实现。这才构成真正的运行时多态。
class B : A
{
    // 使用 new 隐藏基类非虚方法 F()
    new public void F()
    { Console.WriteLine("B.F"); }

    // 使用 override 重写基类虚方法 G()
    public override void G()
    { Console.WriteLine("B.G"); }
}

第3步:测试引用变量的多态行为

通过基类引用变量调用不同状态的方法,观察输出结果。这是理解多态的关键步骤。

class Test
{
    static void F2(A aA) // 接收基类引用
    {
        aA.G(); // 注意这里调用的方法
    }

    static void Main(string[] args)
    {
        B b = new B();       // 创建派生类实例
        A a1 = new A();      // 基类引用指向基类对象
        A a2 = b;            // 基类引用指向派生类对象(多态关键)

        // --- 测试非虚方法 F() ---
        a1.F();  // 输出: A.F (a1是A类型,直接调用A.F)
        a2.F();  // 输出: A.F (a2虽指向B,但F是非虚方法,静态类型为A,故调用A.F)
        b.F();   // 输出: B.F (b是B类型,直接调用B.F)

        // --- 测试虚方法 G() ---
        a1.G();  // 输出: A.G (a1指向A对象,调用A.G)
        a2.G();  // 输出: B.G (a2指向B对象,动态绑定到B.G,体现多态)

        // --- 通过函数传递测试 ---
        F2(a2);  // 输出: B.G (实参是B对象,尽管形参是A类型,仍调用B.G)
        F2(a1);  // 输出: A.G (实参是A对象,调用A.G)
    }
}

上述代码的执行结果直观地展示了:非虚方法看引用类型(静态类型),虚方法看实际对象(动态类型)。

综合案例:员工数据展示

为了更清晰地展示多态在实际业务逻辑中的应用,我们可以构建一个人员管理体系。基类存储通用信息,派生类扩展特定信息,并通过虚方法统一输出。

第4步:定义具有虚方法的基类

基类Person包含姓名和年龄,并定义了一个受保护的虚方法Display用于展示信息。

public class Person
{
    private String name = "张三";
    private int age = 12;

    // 虚方法,允许子类自定义显示内容
    protected virtual void Display()
    {
        Console.WriteLine("姓名:{0}, 年龄:{1}", name, age);
    }

    public Person(string Name, int Age)
    {
        name = Name;
        age = Age;
    }

    // 静态辅助方法,演示如何统一处理不同子类型
    static public void DisplayData(Person aPerson)
    {
        aPerson.Display(); // 依赖多态调用正确的Display实现
    }
}

第5步:在派生类中重写虚方法

Employee类继承自Person,增加部门和薪金字段,并重写Display方法以展示完整信息。

public class Employee : Person
{
    private string department;
    private decimal salary;

    public Employee(string Name, int Age, string D, decimal S) : base(Name, Age)
    {
        department = D;
        salary = S;
    }

    // 重写虚方法
    protected override void Display()
    {
        base.Display(); // 先调用基类显示基础信息
        Console.WriteLine("部门:{0}, 薪金:{1}", department, salary);
    }
}

第6步:验证多态调用

在Main方法中,虽然两个对象的实际类型不同(一个是Person,一个是Employee),但它们都被当作Person类型传递给DisplayData方法。

class Class1
{
    static void Main(string[] args)
    {
        // 普通员工
        Person OnePerson = new Person("李四", 30);
        Person.DisplayData(OnePerson); 
        // 输出: 姓名:李四, 年龄:30

        // 正式雇员
        Employee OneEmployee = new Employee("王五", 40, "财务", 2000);
        Person.DisplayData(OneEmployee);
        // 输出:
        // 姓名:王五, 年龄:40
        // 部门:财务, 薪金:2000
    }
}

这一过程证明了:通过基类引用调用虚方法,系统会根据实际对象的类型,自动分派到对应的子类实现中,从而实现了“一次定义,多种表现”的多态目标。

总结

C#中的多态性是面向对象编程的三大支柱之一。编译时多态通过方法重载实现参数级别的灵活选择;而运行时多态则依赖于virtual和override关键字,允许程序在运行时根据对象的真实类型决定执行哪个方法。掌握new与override的区别,以及静态类型与动态类型的概念,对于避免常见的编程陷阱至关重要。

以上就是C#类多态性的详细内容,更多关于C#面向对象编程的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表