C#教程

关注公众号 jb51net

关闭
首页 > 软件编程 > C#教程 > C#方法重载与重写区别

C#方法重载与重写的7个关键区别和问题详解

作者:墨瑾轩

这篇文章详细探讨了方法重载和重写的7个关键区别,包括定义场景、核心规则、调用时机、应用场景、访问权限规则、返回值类型规则以及重写限制,同时,文章指出重载和重写在编码哲学上的重要性,它们是编程中的两种不同的方法,但有着各自的特点和应用场景

深度剖析重载与重写的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%的候选人说"返回值不同也算重载",结果被我当场问懵了。记住:返回值类型不是重载的判断标准

重写规则:同名同参同返回

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; }
}

注释:重载没有特殊限制,你可以有任意数量的重载方法。但注意:不要过度重载,否则会让代码变得难以维护。就像我在一个办公室里有太多"张三",最后连我自己都分不清谁是谁了。

重写:必须满足特定条件

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() { /* ... */ }
}

问题:父类方法必须是virtualabstract,才能被重写。

正确做法:在父类方法中添加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#方法重载与重写区别的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文