C#中的多态性详解:编译时与运行时实现
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步:在派生类中隐藏或重写方法
派生类对基类方法的处理有两种方式,需严格区分:
- 隐藏非虚方法(new):如果基类方法是普通的非虚方法,子类可以使用
new关键字定义同名方法来“隐藏”它。这不会产生多态效果。 - 重写虚方法(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#面向对象编程的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









