在 C# 里,方法传参默认是按值传递的。也就是说,你传进去的是变量的一个“复印件”,方法里怎么折腾这个副本,都不会影响外面的原始变量。但有时候我们就是想让方法直接“动真格”——操作原始变量本身。这时候就得请出三位“重量级选手”:ref、out 和 in。它们的作用是:按引用传递,让方法和调用方操作的是同一块内存。但三个人性格不同,用法也大不一样。
1. ref 参数——能读能改,真正的“双向奔赴”
ref 是“reference”(引用)的缩写,它能让方法直接读取并修改原始变量。⚠️ 注意:变量必须先初始化才能传给 ref,不然编译器不答应。现实场景:比如银行系统里算利息,方法直接把账户余额拿过来改了,改完外面立刻生效。
using System;
publicclassBank
{
public void ApplyInterest(ref decimal balance, decimal interestRate)
{
balance += balance * interestRate / 100;
Console.WriteLine($"Balance after applying interest: {balance}");
}
}
publicclassProgram
{
public static void Main()
{
decimal accountBalance = 1000m;
Bank bank = new Bank();
Console.WriteLine($"Initial Balance: {accountBalance}");
bank.ApplyInterest(ref accountBalance, 5);
Console.WriteLine($"Final Balance: {accountBalance}");
}
}
优点:
- 方法可以直接改值,不用靠返回值来回传,代码更简洁。
缺点: - 太“自由”了,谁都能改,容易带来意外副作用。
- 必须提前初始化,不够灵活。
2. out 参数——专为“输出”
而生out 的意思是“输出”,它表示这个参数是用来从方法里“带出”值的。
最大的特点:传参前不需要初始化,但方法内部必须给它赋值,否则编译报错。
常见用法:一个方法想返回多个结果。比如登录,既要告诉你成没成功,还想告诉你是什么角色。
using System;
publicclassAuthService
{
public bool Login(string username, string password, out string role)
{
if (username == "admin" && password == "1234")
{
role = "Administrator";
returntrue;
}
elseif (username == "john" && password == "pass")
{
role = "User";
returntrue;
}
else
{
role = "Guest";
returnfalse;
}
}
}
publicclassProgram
{
public static void Main()
{
AuthService auth = new AuthService();
if (auth.Login("admin", "1234", outstring userRole))
{
Console.WriteLine($"Login successful! Role: {userRole}");
}
else
{
Console.WriteLine("Login failed.");
}
}
}
优点:
- 一个方法能返回多个值,特别适合“状态 + 数据”类的场景。
缺点:
- 方法必须保证给 out 参数赋值,逻辑复杂时容易漏掉,维护成本高。
3. in 参数——只读引用,性能利器
in 是 C# 7.2 引入的,意思是“输入”,它按引用传参,但在方法内部是只读的,不能修改。它最大的价值在于:避免大结构体复制带来的性能开销。比如你传一个包含几十个字段的 struct,按值传递会整个拷贝一遍,而 in 只传个“指针”,省时省力。
例子:计算两个坐标点之间的距离。
using System;
publicstruct Coordinates
{
publicdouble X { get; }
publicdouble Y { get; }
public Coordinates(double x, double y)
{
X = x;
Y = y;
}
}
publicclassGeoService
{
public double CalculateDistance(in Coordinates point1, in Coordinates point2)
{
double dx = point2.X - point1.X;
double dy = point2.Y - point1.Y;
return Math.Sqrt(dx * dx + dy * dy);
}
}
publicclassProgram
{
public static void Main()
{
Coordinates location1 = new Coordinates(0, 0);
Coordinates location2 = new Coordinates(3, 4);
GeoService geo = new GeoService();
double distance = geo.CalculateDistance(in location1, in location2);
Console.WriteLine($"Distance: {distance}");
}
}
优点:传大结构体不复制,性能好。
- 只读设计,安全,不用担心被意外修改。
缺点:对 int、decimal 这种小类型没啥意义,反而显得啰嗦。
ref、out、in 对比表,一目了然
| 关键字 | 是否需要提前初始化 | 在方法内是否可修改 | 方法是否必须赋值 | 常用场景 | 优点 | 缺点 |
|---|---|---|---|---|---|---|
| ref | ✅ 必须 | ✅ 可修改 | ❌ 不要求方法需 | 要读取并更新变量 | 无需返回值即可修改变量 | 可能导致意外修改 |
| ou | t❌ 不需要 | ✅ 可修改 | ✅ 必须赋值 | 返回多个结果 | 支持多返回值 | 增加方法复杂度 |
| in | ✅ 必须 | ❌ 只读 | ❌ 不要求 | 大型结构体传参 | 避免复制,性能更好 | 对小数据类型作用不大 |
记忆口诀(背下来,面试不慌)
- ref:读写双全,灵活但危险 → “能进能出,我全都要”
- out:只出不进,专为输出 → “进来是空,出去必有”
- in:只进不改,安全高效 → “进来只看,不准乱动”

总结
ref、out、in 虽然都是按引用传参,但各有各的“人设”:
- ref:要读也要改,变量得先有值。
- out:只负责往外带值,不用初始化,但方法必须给它赋值。
- in:只读模式,适合传大结构体,性能好又安全。用得好,代码更高效、更清晰;用不好,别人看你的代码就像在读天书。所以——能不用就别滥用,该用时也别犹豫。
803

被折叠的 条评论
为什么被折叠?



