深度剖析重载与重写的7个关键区别
区别1:定义场景完全不同
重载(overload):同一个类中的"同名兄弟"
public class calculator
{
// 重载1:两个整数相加
public int add(int a, int b)
{
return a + b;
}
// 重载2:三个整数相加
public int add(int a, int b, int c)
{
return a + b + c;
}
}
注释:重载发生在同一个类中,方法名相同但参数列表不同。就像我在同一个办公室里,有两个同事都叫"张三",但一个负责财务,一个负责销售,名字一样但工作内容完全不同。
重写(override):继承关系中的"父子对决"
public class animal
{
// 基类虚方法
public virtual void makesound()
{
console.writeline("some generic sound");
}
}
public class dog : animal
{
// 重写基类方法
public override void makesound()
{
console.writeline("woof!");
}
}
注释:重写发生在继承关系中,子类覆盖父类的虚方法。就像我有个儿子,名字叫"小墨",他继承了我的胡子,但决定把胡子剪成"闪电形",这就是重写——继承了基础,但改变了实现。
区别2:核心规则完全不同
重载规则:同名不同参
- 方法名必须相同
- 参数列表必须不相同(类型、顺序、个数)
- 返回值类型可以不同(与返回值无关)
public class printer
{
// 重载1:打印字符串
public void print(string message)
{
console.writeline($"printing string: {message}");
}
// 重载2:打印整数
public void print(int number)
{
console.writeline($"printing integer: {number}");
}
}
注释:记住,重载的"不同参"包括类型、顺序、个数,但与返回值无关。我曾经在面试中问过这个问题,90%的候选人说"返回值不同也算重载",结果被我当场问懵了。记住:返回值类型不是重载的判断标准!
重写规则:同名同参同返回
- 方法名必须相同
- 参数列表必须相同
- 返回值类型必须相同
- 必须使用
override关键字 - 父类方法必须是
virtual或abstract
public class animal
{
// 父类虚方法
public virtual void makesound()
{
console.writeline("some generic sound");
}
}
public class dog : animal
{
// 重写父类方法
public override void makesound()
{
console.writeline("woof!");
}
}
注释:重写是"三同"——同名、同参、同返回。这就像你不能在儿子的生日派对上说"祝你生日快乐",然后改成"祝你生日快乐,但用意大利语说",这叫改写,不是重写。重写必须保持方法签名完全一致。
区别3:调用时机完全不同
重载:编译时多态
calculator calc = new calculator(); int sum1 = calc.add(10, 20); // 调用第一个add方法 int sum2 = calc.add(10, 20, 30); // 调用第二个add方法
注释:重载是编译时多态,编译器根据传入的参数类型、个数和顺序,在编译阶段就确定调用哪个方法。就像点餐时,你告诉服务员"我要一份牛肉面",服务员会根据你点的菜名,直接去厨房拿牛肉面,而不是问"你是要牛肉面还是牛肉饭?"。
重写:运行时多态
animal animal = new dog(); animal.makesound(); // 输出"woof!",不是"some generic sound"
注释:重写是运行时多态,在程序运行时,根据对象的实际类型,决定调用哪个方法。就像你去餐厅点"牛肉面",但服务员告诉你"今天牛肉面没了,给你换成牛肉饭",这就是运行时的多态,不是编译时就能确定的。
区别4:应用场景完全不同
重载:简化方法调用
public class stringutils
{
// 重载1:字符串反转
public string reverse(string input)
{
return new string(input.tochararray().reverse().toarray());
}
// 重载2:字符串反转并转换为大写
public string reversetoupper(string input)
{
return reverse(input).toupper();
}
}
注释:重载的目的是简化方法调用,让程序员可以用相同的方法名,处理不同的输入。就像我们去超市,有"苹果"和"苹果汁"两个商品,但我们可以用"买苹果"来指代这两种商品,因为超市会根据你的选择,提供相应的商品。
重写:扩展或修改继承行为
public class vehicle
{
public virtual void start()
{
console.writeline("vehicle started");
}
}
public class car : vehicle
{
public override void start()
{
console.writeline("car started with key");
base.start(); // 调用父类方法
}
}
注释:重写的目的是在继承的基础上扩展或修改行为。就像我有个儿子,他继承了我的胡子,但决定把胡子剪成"闪电形",同时保留了"胡子"这个基本特征。重写不是完全抛弃父类,而是基于父类进行扩展。
区别5:访问权限规则完全不同
重载:无特殊访问权限要求
public class calculator
{
private int add(int a, int b) { return a + b; } // 私有方法
public int add(int a, int b, int c) { return a + b + c; } // 公有方法
}
注释:重载没有特殊的访问权限要求,重载的方法可以有不同的访问修饰符。就像我在同一个办公室里,有"张三"和"张三",一个负责财务,一个负责销售,他们的工作权限可以不同。
重写:访问权限不能低于父类
public class animal
{
public virtual void makesound() { /* ... */ }
}
public class dog : animal
{
// 错误:访问权限低于父类
private override void makesound() { /* ... */ }
}
注释:重写时,子类方法的访问权限不能低于父类。父类是public,子类也必须是public或更开放的权限。这就像我有个儿子,我允许他出去玩,但不能规定他"只能在自己房间玩",因为这比我的权限更小了。
区别6:返回值类型规则完全不同
重载:返回值类型可以不同
public class calculator
{
public int add(int a, int b) { return a + b; }
public string add(string a, string b) { return a + b; }
}
注释:重载时,返回值类型可以不同,这与重写完全不同。我曾经在代码中看到有人因为返回值类型不同,误以为是重载,结果编译报错。记住:重载的返回值类型可以不同,但重写必须相同。
重写:返回值类型必须相同
public class animal
{
public virtual string makesound() { return "some sound"; }
}
public class dog : animal
{
public override string makesound() { return "woof!"; }
}
注释:重写时,返回值类型必须与父类相同。这就像你不能把"苹果"改成"香蕉",然后说"我改了苹果的类型",这叫重写,不是改类型。重写必须保持类型一致。
区别7:重写限制与重载限制完全不同
重载:无特殊限制
public class calculator
{
public int add(int a, int b) { return a + b; }
public int add(int a, int b, int c) { return a + b + c; }
public int add(int a, int b, int c, int d) { return a + b + c + d; }
}
注释:重载没有特殊限制,你可以有任意数量的重载方法。但注意:不要过度重载,否则会让代码变得难以维护。就像我在一个办公室里有太多"张三",最后连我自己都分不清谁是谁了。
重写:必须满足特定条件
- 父类方法必须是
virtual或abstract - 子类方法必须使用
override关键字 - 不能重写
static或private方法
public class animal
{
// 错误:不能重写private方法
private void makesound() { /* ... */ }
}
public class dog : animal
{
// 错误:不能重写private方法
public override void makesound() { /* ... */ }
}
注释:重写有严格的条件限制。只有虚方法和抽象方法才能被重写,static和private方法不能被重写。这就像只有"爸爸"才能"儿子",“儿子"不能"爸爸”,因为"爸爸"是"儿子"的父类。
6个致命坑:你可能正在踩的雷
坑1:误以为重载是运行时多态
calculator calc = new calculator(); calc.add(10, 20); // 调用第一个add方法 calc.add(10, 20, 30); // 调用第二个add方法
问题:有人会误以为重载是运行时多态,就像重写一样。
正确理解:重载是编译时多态,编译器在编译阶段就确定调用哪个方法。
注释:这个坑我踩过,结果在调试时发现方法调用总是不对。后来我才明白,重载不是运行时决定的,而是编译时决定的。现在,我写代码前,都会先想一下:“这是重载,还是重写?”。别问,问就是血泪史。
坑2:在重写方法中忘记使用override关键字
public class animal
{
public virtual void makesound() { /* ... */ }
}
public class dog : animal
{
// 错误:忘记使用override
public void makesound() { /* ... */ }
}
问题:没有使用override关键字,编译器会把它当作一个新的方法,而不是重写。
正确做法:必须使用override关键字。
注释:这个坑我踩过两次,每次都是在深夜赶工的时候。结果代码跑起来,发现子类的方法没有被调用。现在,我写重写方法时,第一件事就是检查"override"关键字是不是写对了。别问,问就是血泪史。
坑3:在重写方法中改变参数列表
public class animal
{
public virtual void makesound(string message) { /* ... */ }
}
public class dog : animal
{
// 错误:参数列表不同
public override void makesound() { /* ... */ }
}
问题:重写时,参数列表必须与父类相同。
正确做法:参数列表必须与父类完全一致。
注释:这个坑我踩过一次,结果在运行时发现子类的方法没有被调用。后来我才明白,重写必须保持参数列表完全一致。现在,我写重写方法时,都会先复制父类的方法签名,再修改内容。别问,问就是血泪史。
坑4:在父类中没有使用virtual关键字
public class animal
{
// 错误:没有使用virtual
public void makesound() { /* ... */ }
}
public class dog : animal
{
// 错误:尝试重写
public override void makesound() { /* ... */ }
}
问题:父类方法必须是virtual或abstract,才能被重写。
正确做法:在父类方法中添加virtual关键字。
注释:这个坑我踩过,结果编译报错,我看了半天才明白是这个原因。现在,我写父类方法时,都会先想一下:“这个方法需要被重写吗?”。需要的话,就加上virtual。别问,问就是血泪史。
坑5:在重写方法中改变返回值类型
public class animal
{
public virtual string makesound() { return "some sound"; }
}
public class dog : animal
{
// 错误:返回值类型不同
public override int makesound() { return 1; }
}
问题:重写时,返回值类型必须与父类相同。
正确做法:返回值类型必须与父类完全一致。
注释:这个坑我踩过,结果在运行时发现方法调用失败。后来我才明白,重写必须保持返回值类型一致。现在,我写重写方法时,都会先复制父类的返回值类型,再修改方法体。别问,问就是血泪史。
坑6:误以为重载可以用于继承关系
public class animal
{
public virtual void makesound() { /* ... */ }
}
public class dog : animal
{
// 错误:这是重写,不是重载
public void makesound(string message) { /* ... */ }
}
问题:在继承关系中,不能用重载来实现"同名不同参",这会被视为重写,但参数列表不同。
正确做法:如果需要在子类中添加新方法,应该使用不同的方法名,或者在父类中添加重载方法。
注释:这个坑我踩过,结果在调用时发现子类的方法没有被调用。后来我才明白,在继承关系中,不能用重载来实现"同名不同参"。现在,我写继承关系时,都会先想一下:“这是重载,还是重写?”。别问,问就是血泪史。
尾声:重载与重写,不只是两个概念,而是一种编码哲学
(咖啡杯空了,烟灰缸满了,但心情却格外清爽)
各位老码农,今天咱们深入探讨了方法重载与重写的7个关键区别和6个致命坑。从"同名"方法的定义场景,到核心规则,再到应用场景、访问权限、返回值类型、限制条件,我相信你已经明白:它们俩不是"双胞胎",而是"兄弟",但性格完全不同。
为什么理解重载和重写这么重要?
因为它让我们从"方法名相同"的混乱中解放出来,让我们可以专注于业务逻辑,而不是代码的样板。它让我们的方法调用看起来更自然,更符合人类的思维习惯。
以上就是c#方法重载与重写的7个关键区别和问题详解的详细内容,更多关于c#方法重载与重写区别的资料请关注代码网其它相关文章!
发表评论