ref 是双向传递,out 是只出不进。

ref 和 out 都是用在方法参数上的关键字,用来改变「参数传递」的方式。之前你在 TryParse 里已经见过 out 了,这篇把 ref 和 out 一起讲清楚。


一、先搞清楚:默认是「复制一份」

方法参数默认按值传递,等于复制一份。

先看一段代码,理解默认行为:

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

class Program
{
static void Main()
{
int x = 10;
Change(x); // 把 x 传进去
Console.WriteLine(x); // 还是 10,没变!
}

static void Change(int n)
{
n = 999; // 改的是 n,不是 x
}
}

运行结果是 10,不是 999。因为传参时,只是把 x 的值复制了一份给 n,改 n 不影响 x。

默认情况下,方法里改参数,不会影响外面的变量。这就是「按值传递」。而 ref 和 out,就是用来打破这个默认规则的。


二、ref:双向传递

ref 让方法内外共用同一个变量。

ref 是「引用传递」,方法里的参数和外面的变量是同一个东西,改了里面,外面也跟着变。

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

class Program
{
static void Main()
{
int x = 10;
Change(ref x); // 加 ref 传进去
Console.WriteLine(x); // 999,变了!
}

static void Change(ref int n)
{
n = 999; // 改 n 就是改 x
}
}
ref 的两个要求

使用 ref 有两个硬性要求:

  • 调用方法时,参数前要写 ref。
  • 方法定义时,参数前也要写 ref。

两处都要写,少一个都编译不过。

ref 传入的变量,在传进去之前必须先有值。比如 int x = 10; 有了值才能用 ref 传。


三、out:只出不进

out 用来让方法把结果传出来。

out 是「输出参数」,专门用来让方法返回多个结果,或者把结果塞到外面的变量里。方法里必须给它赋值。

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

class Program
{
static void Main()
{
int result;
GetValue(out result); // 传 out,result 不用先赋值
Console.WriteLine(result); // 100
}

static void GetValue(out int n)
{
n = 100; // 方法里必须给 n 赋值
}
}
out 的两个要求
  • 调用方法时,参数前写 out。
  • 方法定义时,参数前也写 out。
  • 方法内部,必须给这个 out 参数赋值,不赋值就编译报错。

out 传进去的变量,可以不用提前赋值。因为它的作用就是把值从方法里带出来。


四、ref 和 out 一张表分清

对比项 ref out
传进去前要赋值吗 不要
方法里必须赋值吗 不强制 必须
方法里能读到传入值吗 不能,只能写
作用 双向:进去又出来 只出不进
典型场景 修改外部变量 返回结果

一句话记:ref 要先有值,进去还能读;out 不用先有值,出来才有结果。


五、回顾 TryParse:out 的真实应用

之前学的 TryParse,就是用 out 实现的。

还记得字符串转数值那篇里的 TryParse 吗?它的完整写法里就有 out:

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

class Program
{
static void Main()
{
string input = "123";

// TryParse 第二个参数是 out,用来把结果带出来
bool ok = int.TryParse(input, out int result);

Console.WriteLine(ok); // True
Console.WriteLine(result); // 123
}
}
为什么 TryParse 用 out

TryParse 需要返回两个信息:转换成功没有(用 bool 返回),转换结果是多少(用 out 带出来)。

一个方法只能 return 一个值,所以它用 bool 当返回值表示成功与否,再用 out 把实际结果塞出来。这就是 out 最典型的用途:一个方法要返回多个结果。

out int result 这种写法叫「内联声明」,直接在 out 后面声明变量,不用提前写 int result; 很省事。


六、上位机实战:一个方法返回多个值

上位机里常用 out 让一个方法带回多个数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;

class Program
{
static void Main()
{
// 读一个设备,同时要拿到温度和状态
bool ok = ReadDevice(out double temp, out bool isRunning);

if (ok)
{
Console.WriteLine("温度:" + temp); // 温度:36.5
Console.WriteLine("运行状态:" + isRunning); // 运行状态:True
}
}

// 一个方法同时返回温度和状态两个值
static bool ReadDevice(out double temp, out bool isRunning)
{
temp = 36.5; // 温度
isRunning = true; // 运行状态
return true; // 读取成功
}
}

一个方法用 out 同时带出了温度和状态两个值,这就是 out 在真实项目里的价值。


七、小结

这一篇我们学会了 ref 和 out:

  • 默认传参是复制一份,方法里改不影响外面。
  • ref 双向传递,传入前要有值,方法里能读能改。
  • out 只出不进,传入前不用赋值,方法里必须赋值。
  • TryParse 就是用 out 返回结果。

口诀:

ref 双向要初值,out 单出不用先赋值。

知道默认传参是复制一份,改参数不影响外面

会写 ref 参数,让方法内外共用变量

会写 out 参数,让方法把结果带出来

能说出 ref 和 out 的区别


八、能跑起来的小练习

点我看完整练习代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System;

class Program
{
static void Main()
{
// 1) ref:双向传递
int a = 5;
Double(ref a);
Console.WriteLine(a); // 10

// 2) out:只出不进
int b;
GetTen(out b);
Console.WriteLine(b); // 10

// 3) 一个方法返回多个值
int sum, product;
Calc(3, 4, out sum, out product);
Console.WriteLine("和:" + sum); // 7
Console.WriteLine("积:" + product); // 12
}

static void Double(ref int n)
{
n = n * 2;
}

static void GetTen(out int n)
{
n = 10;
}

static void Calc(int a, int b, out int sum, out int product)
{
sum = a + b;
product = a * b;
}
}