方法进阶
方法重载
方法重载允许在同一个类中定义多个同名方法,只要它们的参数列表不同。参数列表不同指参数个数、类型或顺序不同,返回类型不参与重载判断。
示例
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 开销更低
- 变量作用域:块级 < 方法级 < 类级,内层可访问外层,外层不可访问内层
📝 作业
- 编写三个重载的
Multiply方法,分别接受两个 int、两个 double 和三个 int 参数,返回乘积。 - 编写一个方法
void Log(string msg, string level = "INFO", bool timestamp = true),用命名参数以不同顺序调用它至少三次。 - 编写一个
void Increment(ref int value)方法,将传入的整数加 1,在 Main 中验证变量确实被修改。 - 编写一个
bool TryDivide(int a, int b, out double result)方法,当 b 为 0 时返回 false,否则通过 out 返回除法结果。 - 编写一个
double Average(params double[] values)方法,计算所有参数的平均值,处理空数组情况。 - 用递归实现计算 1+2+3+...+n 的求和方法,并与迭代版本对比结果。
- 编写一个方法,内部使用局部函数来判断一个数是否为质数,并返回 1~50 内所有质数。



