c#里ref和out关键字

在 C# 中,refout 关键字都用于方法参数传递时的引用传递,但它们有一些关键区别。

ref 关键字

作用

ref 关键字用于按引用传递参数,要求参数在传递给方法之前必须初始化。方法内部对参数的修改会影响到调用者的变量。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;

class Program
{
static void Modify(ref int x)
{
x += 10; // 修改传入的变量
}

static void Main()
{
int num = 5; // 变量必须在传递前初始化
Modify(ref num);
Console.WriteLine(num); // 输出 15
}
}

注意ref 参数的值必须在调用方法前赋值,否则编译会报错。


out 关键字

作用

out 关键字用于按引用传递参数,但参数在传递给方法之前可以不初始化,方法内部必须对参数进行赋值。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;

class Program
{
static void GetValues(out int a, out int b)
{
a = 10; // out 参数必须在方法内初始化
b = 20;
}

static void Main()
{
int x, y; // 变量可以不初始化
GetValues(out x, out y);
Console.WriteLine($"x = {x}, y = {y}"); // 输出 x = 10, y = 20
}
}

注意:调用 out 方法前,参数可以不初始化,但方法内部必须给 out 参数赋值,否则编译错误。


refout 的区别

关键点 ref out
传递方式 按引用传递 按引用传递
传递前是否需要初始化 需要 不需要
方法内部是否必须赋值 不强制 必须赋值
适用场景 需要传递已初始化的变量,并可能修改其值 适用于返回多个值的情况

refout 的应用场景

ref 适用场景

  • 修改原始值(如修改方法外的变量值)
  • 传递大对象,避免值传递带来的性能开销

out 适用场景

  • 返回多个值(如 TryParse 方法)
  • 不关心传入值,只关心返回值

refout 在方法重载中的区别

C# 允许 refout 作为方法重载的区分因素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Program
{
static void Test(ref int x) { Console.WriteLine("ref 方法"); }
static void Test(out int x) { x = 10; Console.WriteLine("out 方法"); }

static void Main()
{
int a = 5;
Test(ref a); // 调用 ref 方法

int b;
Test(out b); // 调用 out 方法
}
}

TryParse 方法的 out 典型应用

C# 中 int.TryParse() 就是 out 的一个典型应用:

1
2
3
4
5
6
7
8
9
int result;
if (int.TryParse("123", out result))
{
Console.WriteLine($"转换成功: {result}");
}
else
{
Console.WriteLine("转换失败");
}

这里 TryParse 不需要 result 预先赋值,它在方法内部会赋值。


7. ref readonly(C# 7.2 引入)

如果你想用 ref不希望方法内部修改传入的值,可以使用 ref readonly

1
2
3
4
static void PrintValue(ref readonly int x)
{
Console.WriteLine(x); // 只能读取,不能修改 x
}

这在高性能场景(如结构体传递)中可以减少值复制的开销。


总结

  • ref:必须初始化,方法内部可以修改参数值。
  • out:不需要初始化,方法内部必须赋值
  • ref readonly:按引用传递但只读
  • out 适用于返回多个值,ref 适用于修改变量。