C#每日面试题-重写和重载的区别
在C#面向对象编程中,"重写(Override)"和"重载(Overload)"是两个高频出现的概念,也是面试中的核心考点。很多初学者容易混淆二者,其实它们的设计目的、使用场景和语法规则有着本质区别。今天我们就从"是什么""怎么用""核心区别"三个维度,用最简单的语言把这两个概念讲透。
一、先搞懂:重写(Override)是什么?
重写的核心是"子类重新实现父类的方法 ",前提是父类方法被标记为可重写(用 virtual、abstract 或 override 修饰)。它的核心目的是:在继承体系中,让子类根据自身特性定制父类的通用行为,实现"多态"。
1. 重写的语法规则(必须遵守)
-
父类方法必须用
virtual(虚方法)或abstract(抽象方法)修饰(抽象方法必须重写); -
子类方法必须用
override关键字声明,且方法签名(方法名、参数列表、返回值类型)必须和父类完全一致; -
重写只能发生在"子类与父类"的继承关系中;
-
不能重写非虚方法(无
virtual修饰的普通方法)。
2. 重写的实例(一看就懂)
csharp
// 父类:动物
public class Animal
{
// 虚方法:父类定义通用行为
public virtual void MakeSound()
{
Console.WriteLine("动物发出叫声");
}
}
// 子类:狗(继承动物)
public class Dog : Animal
{
// 重写父类方法:定制狗的叫声
public override void MakeSound()
{
Console.WriteLine("汪汪汪!");
}
}
// 子类:猫(继承动物)
public class Cat : Animal
{
// 重写父类方法:定制猫的叫声
public override void MakeSound()
{
Console.WriteLine("喵喵喵!");
}
}
// 测试:多态效果
public class Test
{
public static void Main()
{
Animal animal1 = new Dog();
Animal animal2 = new Cat();
animal1.MakeSound(); // 输出:汪汪汪!(调用子类重写的方法)
animal2.MakeSound(); // 输出:喵喵喵!(调用子类重写的方法)
}
}
从实例能看出:父类 Animal 定义了通用的 MakeSound 方法,子类 Dog 和 Cat 通过重写,实现了符合自身特性的行为。当用父类引用指向子类对象时,会自动调用子类的重写方法------这就是重写实现的"多态"核心价值。
二、再搞懂:重载(Overload)是什么?
重载的核心是"同一个类中,存在多个同名但参数不同的方法 "。它的核心目的是:为了让方法更灵活,用同一个方法名处理不同类型/数量的参数,减少方法名冗余。
1. 重载的语法规则(必须遵守)
-
方法名必须完全相同;
-
参数列表必须不同(参数类型、参数个数、参数顺序,满足其一即可);
-
返回值类型、访问修饰符可以不同(但不能仅凭返回值类型区分重载,因为调用时无法指定返回值类型);
-
重载可以发生在同一个类中,也可以发生在子类继承父类时(子类重载父类的方法)。
2. 重载的实例(一看就懂)
csharp
// 计算器类:用重载实现不同类型的加法
public class Calculator
{
// 1. 两个int类型相加
public int Add(int a, int b)
{
return a + b;
}
// 2. 三个int类型相加(参数个数不同,重载)
public int Add(int a, int b, int c)
{
return a + b + c;
}
// 3. 两个double类型相加(参数类型不同,重载)
public double Add(double a, double b)
{
return a + b;
}
// 4. int和double类型相加(参数顺序+类型不同,重载)
public double Add(int a, double b)
{
return a + b;
}
}
// 测试:调用不同的重载方法
public class Test
{
public static void Main()
{
Calculator calc = new Calculator();
Console.WriteLine(calc.Add(1, 2)); // 调用Add(int, int),输出3
Console.WriteLine(calc.Add(1, 2, 3)); // 调用Add(int, int, int),输出6
Console.WriteLine(calc.Add(1.5, 2.5)); // 调用Add(double, double),输出4.0
Console.WriteLine(calc.Add(1, 2.5)); // 调用Add(int, double),输出3.5
}
}
从实例能看出:Calculator类中只有一个"Add"方法名,但通过不同的参数列表,实现了"不同场景下的加法"。调用时,编译器会根据你传入的参数类型、个数,自动匹配对应的重载方法------这就是重载的核心价值:简化方法调用,让代码更简洁。
三、核心区别:一张表总结(面试直接用)
| 对比维度 | 重写(Override) | 重载(Overload) |
|---|---|---|
| 核心目的 | 子类定制父类行为,实现多态 | 灵活处理不同参数,简化方法调用 |
| 存在范围 | 子类与父类(继承关系中) | 同一个类,或子类与父类(继承中) |
| 方法签名 | 必须完全相同(方法名、参数、返回值) | 必须不同(参数类型/个数/顺序,选其一) |
| 关键字要求 | 父类用virtual/abstract,子类用override | 无需特殊关键字 |
| 调用逻辑 | 运行时绑定(根据对象实际类型匹配) | 编译时绑定(根据传入参数匹配) |
四、面试易错点:避开这2个坑
1. 坑1:认为"返回值不同"可以实现重载
错误示例:
csharp
// 错误:仅凭返回值类型不同,不能构成重载
public int Add(int a, int b) { return a + b; }
public double Add(int a, int b) { return a + b; }
原因:编译器调用方法时,不会根据"你希望的返回值类型"来匹配,所以无法区分这两个方法,会直接报错。
2. 坑2:重写时修改参数列表
错误示例:
csharp
public class Animal
{
public virtual void MakeSound(string name)
{
Console.WriteLine($"{name}发出叫声");
}
}
// 错误:重写时修改了参数列表,实际是重载,不是重写
public class Dog : Animal
{
public override void MakeSound() // 编译报错:方法签名不匹配
{
Console.WriteLine("汪汪汪");
}
}
原因:重写要求方法签名完全一致,若修改参数列表,本质是在子类中定义了一个新的方法(重载),而非重写父类方法。
五、总结
记住核心区别:重写是"同名同参同返回,子类改父类行为",为了多态;重载是"同名不同参,灵活处理参数",为了简洁。
面试时,只要先讲清二者的定义和目的,再结合表格对比核心维度,最后举一个简单实例,就能清晰地展现你的理解。如果面试官追问"运行时绑定和编译时绑定的区别",再补充:重写是运行时绑定(对象实际类型决定),重载是编译时绑定(参数决定),基本就能拿到满分啦!