C#ref /out 详解 + 传统写法 + 元组替代方案
C#ref /out 详解 + 传统写法 + 元组替代方案
统一项目、精简代码、分类注释、适配 C#7.3,剔除冗余命名空间、重复结构,按知识点模块拆分,循序渐进,方便学习。
整体分为 4 大模块:
- 基础值传递 vs
ref引用传递 out输出参数(基础用法 + 模拟 TryParse + 多返回值)ref + out混合使用- C#7 元组替代
ref/out(现代推荐写法)
using System;
using System.Collections.Generic;
using System.Linq;
namespace RefOutDemo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("========== 1. 普通值传递 ==========\n");
TestValuePass();
Console.WriteLine("\n========== 2. ref 基础用法 ==========\n");
TestRefBasic();
Console.WriteLine("\n========== 3. out 基础用法 ==========\n");
TestOutBasic();
Console.WriteLine("\n========== 4. 模拟 int.TryParse(out 经典场景) ==========\n");
TestCustomTryParse();
Console.WriteLine("\n========== 5. out 实现多返回值(数组计算) ==========\n");
TestArrayCalcByOut();
Console.WriteLine("\n========== 6. ref + out 混合使用 ==========\n");
TestRefAndOutMix();
Console.WriteLine("\n========== 7. C#7.3 元组 替代 ref/out ==========\n");
TestTupleReplaceRefOut();
Console.ReadKey();
}
#region 1. 普通值传递
/// <summary>
/// 普通值传递:传递变量副本,修改不影响原数据
/// </summary>
static void TestValuePass()
{
int num = 10;
ChangeValue(num);
Console.WriteLine($"普通值传递结果:{num}");
}
static void ChangeValue(int a)
{
a = 100;
}
#endregion
#region 2. ref 基础用法
/// <summary>
/// ref 引用传递:操作原变量地址,调用前变量必须初始化
/// </summary>
static void TestRefBasic()
{
// 示例1
int x = 10;
ReverseValueRef(ref x);
Console.WriteLine($"ref 示例1结果:{x}");
// 示例2
int number = 10;
Test(ref number);
Console.WriteLine($"ref 示例2结果:{number}");
}
static void ReverseValueRef(ref int value)
{
value = 20;
}
static void Test(ref int a)
{
a = 500;
}
#endregion
#region 3. out 基础用法
/// <summary>
/// out 输出参数:用于向外传值,调用前可不用初始化,方法内必须赋值
/// </summary>
static void TestOutBasic()
{
// 基础out演示
int y;
ReverseValueOut(out y);
Console.WriteLine($"out 基础结果:{y}");
// 方法返回值 + out 组合
int num;
int res = TestOutReturn(out num);
Console.WriteLine($"返回值:{res},out传出值:{num}");
}
static void ReverseValueOut(out int value)
{
value = 30;
}
static int TestOutReturn(out int a)
{
a = 10;
return 200;
}
#endregion
#region 4. 模拟 int.TryParse(out 经典场景)
static void TestCustomTryParse()
{
string str1 = "123aa";
int result;
// 系统自带方法
if (int.TryParse(str1, out result))
Console.WriteLine($"系统转换成功:{result}");
else
Console.WriteLine($"系统转换失败,默认值:{result}");
// 自定义方法
string str2 = "123aaa";
if (MyIntTryParse(str2, out result))
Console.WriteLine($"自定义转换成功:{result}");
else
Console.WriteLine("自定义转换失败");
}
static bool MyIntTryParse(string s, out int result)
{
result = 0;
try
{
result = Convert.ToInt32(s);
return true;
}
catch
{
return false;
}
}
#endregion
#region 5. out 实现多返回值(数组计算)
static void TestArrayCalcByOut()
{
int[] nums = { 3, 4, 2, 5, 1, 6 };
int max, min;
int sum = CalcArray(nums, out max, out min);
Console.WriteLine($"数组和:{sum},最大值:{max},最小值:{min}");
}
static int CalcArray(int[] numbers, out int max, out int min)
{
if (numbers == null || numbers.Length == 0)
throw new ArgumentException("数组不能为空");
int sum = 0;
max = numbers[0];
min = numbers[0];
foreach (var item in numbers)
{
sum += item;
if (item > max) max = item;
if (item < min) min = item;
}
return sum;
}
#endregion
#region 6. ref + out 混合使用
static void TestRefAndOutMix()
{
int sum = 0;
int product;
CalcTwoNum(5, 3, ref sum, out product);
Console.WriteLine($"两数之和:{sum},两数之积:{product}");
}
static void CalcTwoNum(int a, int b, ref int sum, out int product)
{
sum = a + b;
product = a * b;
}
#endregion
#region 7. C#7.3 元组 替代 ref/out
static void TestTupleReplaceRefOut()
{
int[] arr = { 1, 2, 3, 4, 5, 6 };
var data = GetMaxMinAvg(arr);
Console.WriteLine($"最大值:{data.max},最小值:{data.min},平均值:{data.average}");
// 元组解构
var (maxVal, minVal, avgVal) = GetMaxMinAvg(arr);
Console.WriteLine($"解构取值 -> 最大:{maxVal},最小:{minVal},平均:{avgVal}");
}
static (int max, int min, double average) GetMaxMinAvg(IEnumerable<int> nums)
{
return (nums.Max(), nums.Min(), nums.Average());
}
#endregion
}
}
一、核心知识点总结(配合代码学习)
1. 普通值传递
- 传递的是变量副本,方法内修改不影响外部原变量。
2. ref 引用参数
- 作用:传递变量本身地址,方法内外操作同一个变量
- 强制规则:
- 调用方法前,变量必须手动初始化
- 方法定义和调用处,都必须写
ref
3. out 输出参数(重点)
- 作用:专门用来向外输出多个结果
- 强制规则:
- 调用前变量可以不初始化
- 方法内部所有分支必须给 out 参数赋值(编译强制校验)
- 经典场景:
int.TryParse、DateTime.TryParse等
4. ref vs out 对比
| 关键字 | 调用前是否要初始化 | 主要用途 |
|---|---|---|
| ref | 必须初始化 | 双向传值(传入+传出) |
| out | 可不初始化 | 单向传出(只用来返回结果) |
5. C#7.3 元组(推荐替代方案)
- 老旧代码常用
ref/out做多返回值; - 新项目优先使用值元组,语义清晰、代码整洁、可读性远高于
out。
二、运行输出结果
========== 1. 值传递 VS ref 引用传递 ==========
普通值传递结果:10
ref 引用传递结果:20
========== 2. ref 基础用法 ==========
========== 3. out 基础用法 ==========
out 基础用法结果:30
方法返回值:200,out 传出值:10
========== 4. 模拟 int.TryParse(out 经典场景) ==========
系统转换失败,默认值:0
自定义转换失败
========== 5. out 实现方法多返回值(数组计算) ==========
数组和:21,最大值:6,最小值:1
========== 6. ref + out 混合使用 ==========
两数之和:8,两数之积:15
========== 7. C#7.3 元组 替代 ref/out(推荐现代写法) ==========
最大值:6,最小值:1,平均值:3.5
解构取值 -> 最大:6,最小:1,平均:3.5
三、学习建议
- 先看懂 值传递 → ref → out 三步递进,理解「副本」和「引用」区别;
- 重点吃透
TryParse模拟案例,这是out最真实的业务用法; - 最后学习元组,以后实际开发尽量用元组代替
out做多返回值; - 代码按区域折叠,逐个模块调试,单步执行观察变量变化,理解更透彻。

浙公网安备 33010602011771号