404 Not Found

404 Not Found


nginx

方法进阶

方法重载

方法重载允许在同一个类中定义多个同名方法,只要它们的参数列表不同。参数列表不同指参数个数、类型或顺序不同,返回类型不参与重载判断。

示例

CSHARP
using System;

class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public double Add(double a, double b)
    {
        return a + b;
    }

    public int Add(int a, int b, int c)
    {
        return a + b + c;
    }
}

class Program
{
    static void Main()
    {
        Calculator calc = new Calculator();
        Console.WriteLine(calc.Add(1, 2));
        Console.WriteLine(calc.Add(1.5, 2.5));
        Console.WriteLine(calc.Add(1, 2, 3));
    }
}
▶ 试一试
TEXT
3
4
6
💡 提示: 仅返回类型不同不能构成重载,编译器会报错。

可选参数与命名参数

可选参数在方法声明时为参数指定默认值,调用时可以省略这些参数。命名参数通过指定参数名来传递值,不必按顺序传递。

示例

CSHARP
using System;

class Program
{
    static void Print(string msg, int count = 1, string prefix = "")
    {
        for (int i = 0; i < count; i++)
        {
            Console.WriteLine($"{prefix}{msg}");
        }
    }

    static void Main()
    {
        Print("Hello");
        Print("Hi", 3);
        Print(msg: "World", prefix: "> ");
        Print(prefix: ">> ", count: 2, msg: "Test");
    }
}
▶ 试一试
TEXT
Hello
Hi
Hi
Hi
> World
>> Test
>> Test
⚠️ 注意: 可选参数必须放在所有必选参数之后。

ref 参数修饰符

ref 关键字使参数按引用传递,方法内对参数的修改会影响调用方的变量。调用前必须初始化变量,调用时也必须使用 ref 关键字。

示例

CSHARP
using System;

class Program
{
    static void Swap(ref int a, ref int b)
    {
        int temp = a;
        a = b;
        b = temp;
    }

    static void Main()
    {
        int x = 10;
        int y = 20;
        Console.WriteLine($"Before: x={x}, y={y}");
        Swap(ref x, ref y);
        Console.WriteLine($"After: x={x}, y={y}");
    }
}
▶ 试一试
TEXT
Before: x=10, y=20
After: x=20, y=10

out 参数修饰符

out 关键字同样按引用传递,但方法内必须为 out 参数赋值,调用前不需要初始化变量。常用于需要返回多个值的场景。

示例

CSHARP
using System;

class Program
{
    static bool TryParseInt(string s, out int result)
    {
        result = 0;
        foreach (char c in s)
        {
            if (c < '0' || c > '9')
                return false;
            result = result * 10 + (c - '0');
        }
        return true;
    }

    static void Main()
    {
        if (TryParseInt("123", out int number))
        {
            Console.WriteLine($"Parsed: {number}");
        }

        if (!TryParseInt("abc", out int fail))
        {
            Console.WriteLine("Parse failed");
        }
    }
}
▶ 试一试
TEXT
Parsed: 123
Parse failed
📌 要点: out 参数在方法返回前必须赋值,C# 7 起允许在调用时直接声明变量。

in 参数修饰符

in 关键字使参数按引用传递但为只读,方法内不能修改 in 参数的值。适用于传递大型结构体时避免拷贝开销,同时防止意外修改。

示例

CSHARP
using System;

struct BigData
{
    public long A, B, C, D, E, F, G, H;
}

class Program
{
    static void Process(in BigData data)
    {
        Console.WriteLine($"A={data.A}, B={data.B}");
    }

    static void Main()
    {
        BigData d = new BigData { A = 100, B = 200 };
        Process(in d);
        Process(d);
    }
}
▶ 试一试
TEXT
A=100, B=200
A=100, B=200

ℹ️ 说明: 调用时 in 关键字可以省略,编译器会自动按引用传递。

params 可变参数

params 关键字允许方法接受可变数量的参数,编译器将它们收集为数组。一个方法只能有一个 params 参数,且必须放在参数列表最后。

示例

CSHARP
using System;

class Program
{
    static int Sum(params int[] numbers)
    {
        int total = 0;
        foreach (int n in numbers)
        {
            total += n;
        }
        return total;
    }

    static void Main()
    {
        Console.WriteLine(Sum(1, 2, 3));
        Console.WriteLine(Sum(10, 20, 30, 40, 50));
        Console.WriteLine(Sum());
    }
}
▶ 试一试
TEXT
6
150
0
⚠️ 注意: params 只能用于一维数组,且一个方法最多只能有一个 params 参数。

递归原理与经典案例

递归是方法调用自身的编程技术。每个递归必须有一个终止条件(基线条件),否则会导致栈溢出。

阶乘

CSHARP
using System;

class Program
{
    static long Factorial(int n)
    {
        if (n <= 1)
            return 1;
        return n * Factorial(n - 1);
    }

    static void Main()
    {
        for (int i = 1; i <= 10; i++)
        {
            Console.WriteLine($"{i}! = {Factorial(i)}");
        }
    }
}
TEXT
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800

斐波那契数列

CSHARP
using System;

class Program
{
    static int Fibonacci(int n)
    {
        if (n <= 0) return 0;
        if (n == 1) return 1;
        return Fibonacci(n - 1) + Fibonacci(n - 2);
    }

    static void Main()
    {
        for (int i = 0; i <= 10; i++)
        {
            Console.Write($"{Fibonacci(i)} ");
        }
        Console.WriteLine();
    }
}
TEXT
0 1 1 2 3 5 8 13 21 34 55
🔥 性能警告: 朴素递归计算斐波那契数列存在大量重复计算,时间复杂度为 O(2^n)。实际开发中应使用迭代或记忆化优化。

局部函数

C# 7 引入局部函数,允许在方法内部定义方法。局部函数只能在其包含方法内访问,适用于提取只在特定方法中使用的辅助逻辑。

示例

CSHARP
using System;

class Program
{
    static int[] GetEvenSquares(int[] numbers)
    {
        bool IsEven(int n) => n % 2 == 0;
        int Square(int n) => n * n;

        System.Collections.Generic.List<int> result = new System.Collections.Generic.List<int>();
        foreach (int n in numbers)
        {
            if (IsEven(n))
            {
                result.Add(Square(n));
            }
        }
        return result.ToArray();
    }

    static void Main()
    {
        int[] data = { 1, 2, 3, 4, 5, 6, 7, 8 };
        int[] result = GetEvenSquares(data);
        Console.WriteLine(string.Join(", ", result));
    }
}
▶ 试一试
TEXT
4, 16, 36, 64
💡 提示: 局部函数可以捕获包含方法中的局部变量,且比 lambda 表达式开销更低。

变量作用域

变量的作用域决定了它可以在哪里被访问。C# 中主要有三种作用域级别:块级作用域(if/for/while 等代码块内)、方法级作用域(方法内定义的局部变量)、类级作用域(类中定义的字段)。

示例

CSHARP
using System;

class ScopeDemo
{
    int classField = 100;

    void MethodA()
    {
        int localVar = 10;
        Console.WriteLine($"classField={classField}, localVar={localVar}");

        if (true)
        {
            int blockVar = 20;
            Console.WriteLine($"blockVar={blockVar}");
        }
    }

    void MethodB()
    {
        Console.WriteLine($"classField={classField}");
    }
}

class Program
{
    static void Main()
    {
        ScopeDemo demo = new ScopeDemo();
        demo.MethodA();
        demo.MethodB();
    }
}
▶ 试一试
TEXT
classField=100, localVar=10
blockVar=20
classField=100

规则: 同一方法中不能声明同名局部变量,内层代码块可以访问外层变量,但外层无法访问内层块变量。

❓ 常见问题

Q 方法重载能仅靠返回类型区分吗?
A 不能,编译器根据调用时的参数匹配方法,返回类型不参与签名判断。 Q:ref 和 out 的核心区别是什么? A:ref 要求调用前初始化,out 要求方法内赋值;ref 传入已有值可被修改,out 通常用于返回额外结果。 Q:params 参数可以为 null 吗? A:可以,params 本质是数组,传 null 不会自动创建空数组,方法内需要判空。 Q:递归一定比迭代慢吗? A:不一定,但递归有栈深度限制和调用开销,深度递归可能导致 StackOverflowException。 Q:局部函数和 lambda 表达式有何区别? A:局部函数是真正的方法,无委托开销,支持泛型和 ref/out;lambda 生成委托或表达式树,有额外分配。

📖 小节

  • 方法重载:同名不同参,返回类型不参与
  • 可选参数:声明时赋默认值,必须放在必选参数后
  • 命名参数:通过参数名传值,打破位置限制
  • ref:按引用传递,调用前必须初始化
  • out:按引用传递,方法内必须赋值,调用前无需初始化
  • in:只读引用传递,防止修改且避免拷贝
  • params:可变数量参数,编译为数组,只能有一个且放最后
  • 递归:方法调用自身,必须有终止条件,注意性能和栈溢出
  • 局部函数:方法内定义方法,C# 7+ 特性,比 lambda 开销更低
  • 变量作用域:块级 < 方法级 < 类级,内层可访问外层,外层不可访问内层

📝 作业

  1. 编写三个重载的 Multiply 方法,分别接受两个 int、两个 double 和三个 int 参数,返回乘积。
  2. 编写一个方法 void Log(string msg, string level = "INFO", bool timestamp = true),用命名参数以不同顺序调用它至少三次。
  3. 编写一个 void Increment(ref int value) 方法,将传入的整数加 1,在 Main 中验证变量确实被修改。
  4. 编写一个 bool TryDivide(int a, int b, out double result) 方法,当 b 为 0 时返回 false,否则通过 out 返回除法结果。
  5. 编写一个 double Average(params double[] values) 方法,计算所有参数的平均值,处理空数组情况。
  6. 用递归实现计算 1+2+3+...+n 的求和方法,并与迭代版本对比结果。
  7. 编写一个方法,内部使用局部函数来判断一个数是否为质数,并返回 1~50 内所有质数。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏