ref要求调用前初始化,方法可读可写;out允许调用前未初始化,但方法内必须赋值且不可读初始值;二者不可仅凭ref/out重载,数组传参时ref可替换整个引用而out必须新建并赋值。

ref 和 out 都能让方法修改调用方的变量,但它们的语义和约束完全不同——用错一个,编译器立刻报错;用对了,才能安全返回多个值或原地更新状态。
ref 参数必须在调用前初始化
ref 是“双向引用”:调用方负责提供一个已赋值的变量,方法内可以读、也可以改,改完直接反映到原变量上。
- 不初始化就传
ref,编译失败,错误信息是Use of unassigned local variable - 方法内部不一定要改它,但可以读它的初始值(
out不行) - 适用于需要“先有值、再参与计算、最后覆盖”的场景,比如数值累加、交换、状态切换
示例:
void Increment(ref int value) {
value = value + 1; // 可读可写
}
int x = 5;
Increment(ref x); // ✅ 正确:x 已初始化
// Increment(ref y); // ❌ 编译错误:y 未赋值
out 参数必须在方法内赋值
out 是“单向输出”:调用方可以传一个未初始化的变量,但方法体里必须给它赋一次值,且只能赋值(不能读取初始值)。
- 方法返回前没给
out参数赋值 → 编译错误:Use of unassigned local variable - 调用时也必须显式写
out关键字,不能省略 - 典型用途是“一个方法返回多个结果”,比如解析字符串、TryParse 模式
示例:
bool TryParseInt(string s, out int result) {
if (int.TryParse(s, out result)) { // ✅ result 在这里被赋值
return true;
}
result = 0; // ❗必须保证所有路径都赋值,哪怕走 else
return false;
}
string input = "42";
if (TryParseInt(input, out int num)) {
Console.WriteLine(num); // 输出 42
}
ref 和 out 不能仅靠修饰符重载
如果两个方法只有 ref 和 out 的区别,其他签名完全一样,C# 禁止编译——因为调用端语法几乎一致,容易引发歧义。
- 下面这段代码会触发编译错误
CS0663: void Process(ref int x) { }void Process(out int x) { x = 1; }- 但允许和无修饰符版本共存:
void Process(int x)和void Process(out int x)可以同时存在
数组传参时 ref 和 out 的行为差异更明显
传数组时,ref 和 out 的区别不是“能不能改元素”,而是“能不能换整个数组对象”。
-
out int[] arr:调用方声明即可,不初始化;方法必须 new 一个新数组并赋给arr -
ref int[] arr:调用方必须提供一个已创建的数组;方法可以改元素,也可以arr = new int[10]替换整个引用 - 注意:
out数组在方法内不能访问arr.Length等属性(因为还没赋值),而ref可以
容易忽略的一点:即使你只打算改数组内容,也不该默认选 ref——如果调用方本意是“我传个空引用进去,你给我填满”,那 out 才是语义正确的选择。










