| 方式 | 描述 |
|---|---|
| 值传递 | 值传递会复制参数的实际值并赋值给函数的形式参数,实参和形参使用的是两个不同内存位置中的值,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全 |
| 引用传递 | 引用传递会复制参数的内存位置并传递给形式参数,当形参的值发生改变时,同时也会改变实参的值 |
| 输出传递 | 输出传递可以一次返回多个值 |
值传递过于简单,不讲
✅ref关键字
ref关键字用于按引用传递参数。这意味着不是传递参数的值,而是传递该参数的内存地址。因此,任何对参数的修改都会影响到原始变量。这与按值传递形成对比,在按值传递中,方法接收到的是参数的一个副本,对副本的修改不会影响原始变量。
📌使用场景
ref关键字主要用于以下几种情况:
- 按引用传递参数:允许方法修改调用者提供的参数,并让这些修改反映在调用者的环境中。
- 返回引用类型的数据:从C# 7.0开始,
ref还可以用于返回一个引用(通过ref return),并可以与局部变量一起使用来存储这些引用(通过ref local)。
📌基本语法
方法声明时使用ref当定义,如果希望某些参数以引用的方式传递,则需要在参数列表中指定ref关键字。
调用方法时使用ref在调用带有ref参数的方法时,也需要在实际参数前加上ref关键字,并且传入的参数必须是变量(不能是常量或表达式)。
int myNumber = 5;
ModifyValue(ref myNumber);
Console.WriteLine(myNumber); // 输出: 15
📌示例代码
这里有一个简单的例子展示了如何使用ref关键字:
using System;
class Program
{
static void Main()
{
int a = 1, b = 2;
Console.WriteLine($"Before swap: a = {a}, b = {b}");
Swap(ref a, ref b);
Console.WriteLine($"After swap: a = {a}, b = {b}");
}
static void Swap(ref int x, ref int y)
{
int temp = x;
x = y;
y = temp;
}
}
输出结果:
Before swap: a = 1, b = 2
After swap: a = 2, b = 1
📌C# 7.0及之后的ref增强功能
自C# 7.0以来,ref关键字得到了扩展,支持了ref returns(引用返回)和ref locals(引用本地变量)。这使得函数可以返回对数据的引用而不是其副本,同时也允许创建指向这些引用的本地变量。
ref return示例
using System;
class RefExample
{
private static int[] array = new int[] { 1, 2, 3, 4 };
public static ref int Find(int target) // 返回数组中第一个匹配项的引用
{
for (int i = 0; i < array.Length; i++)
{
if (array[i] == target)
return ref array[i];
}
throw new ArgumentOutOfRangeException(nameof(target), "Target value not found");
}
static void Main()
{
ref int result = ref Find(3); // 查找数字3
result = 0; // 修改找到的元素
foreach (var item in array)
{
Console.WriteLine(item);
}
// 输出:
// 1
// 2
// 0
// 4
}
}
✅out关键字
🧠 一、什么是 out 参数?
在 C# 中,out 是一个关键字,用于将参数按引用传递,但它与 ref 不同:
ref:要求变量在传入前必须已经赋值。out:不要求变量在传入前赋值,但必须在方法内部赋值。
也就是说,out 主要用于从方法中传出值,而不是传入值再修改它。
🧠 二、基本语法
方法定义时使用 out
public void GetData(out int result)
{
result = 42; // 必须赋值
}
调用时使用 out
int value;
GetData(out value);
Console.WriteLine(value); // 输出 42
注意:调用时也必须写
out,即使变量已经声明。
🔁 三、为什么需要 out?应用场景有哪些?
场景 1:返回多个值
C# 方法默认只能返回一个值(通过 return)。如果想返回多个值,可以使用 out 参数。
public void GetCoordinates(out int x, out int y)
{
x = 100;
y = 200;
}
调用:
int a, b;
GetCoordinates(out a, out b);
Console.WriteLine($"x: {a}, y: {b}"); // 输出 x: 100, y: 200
场景 2:Try 模式(TryParse 等)
这是 .NET 中非常常见的设计模式,用于避免异常处理带来的性能开销。
例如:
string input = "123";
int number;
bool success = int.TryParse(input, out number);
if (success)
{
Console.WriteLine($"成功解析: {number}");
}
else
{
Console.WriteLine("输入不是数字");
}
TryParse返回布尔值表示是否成功,而真正的结果通过out参数返回。
场景 3:性能优化(避免频繁创建对象)
比如在游戏开发或高性能场景中,你可以复用对象并通过 out 参数填充数据。
public bool TryGetPlayerStats(int id, out PlayerStats stats)
{
if (_playerCache.TryGetValue(id, out stats))
{
return true;
}
// 如果缓存中没有,则从数据库加载
stats = LoadFromDatabase(id);
_playerCache[id] = stats;
return true;
}
⚠️ 四、注意事项 & 常见错误
❌ 错误 1:未在方法内赋值
void BadMethod(out int x)
{
// 编译错误!out 参数必须赋值
}
✅ 正确做法:
void GoodMethod(out int x)
{
x = 0; // 必须赋值
}
❌ 错误 2:调用时不写 out
int val;
GetValue(val); // 错误!应该写 out val
✅ 正确写法:
GetValue(out val);
❌ 错误 3:试图传入表达式或常量
GetValue(out 123); // ❌ 错误
GetValue(out Math.PI); // ❌ 错误
✅ 只能传入变量:
int val;
GetValue(out val); // ✅ 正确
💡 五、C# 7.0+ 的改进:out 变量直接声明
从 C# 7.0 开始,可以在调用时直接声明 out 变量,无需提前声明:
if (int.TryParse("123", out int num))
{
Console.WriteLine(num);
}
这样写更简洁,也减少了作用域污染。
📦 六、与 ref 的对比总结
| 特性 | out | ref |
|---|---|---|
| 是否需要初始化 | 否(但必须在方法内赋值) | 是(必须在调用前赋值) |
| 使用目的 | 从方法传出值 | 传入并可能被修改的值 |
| 调用时是否要加关键字 | 是 | 是 |
| 是否可重载 | 是(void Foo(ref int) 和 void Foo(out int) 是不同方法) | 是 |
| 是否允许只读字段作为参数 | ❌ | ✅(如果字段是 readonly 但类型是结构体) |
🧩 七、高级用法(C# 7.0+)
ref locals(引用本地变量) + out
虽然不常见,但你可以结合 ref 和 out 来操作引用。
Span<int> numbers = stackalloc int[] { 1, 2, 3 };
ref int found = ref FindValue(2, out int index);
Console.WriteLine($"Found at index {index}: {found}");
// 修改原数组中的值
found = 99;
Console.WriteLine(string.Join(", ", numbers)); // 输出: 1, 99, 3
out 是一种“只出不进”的参数传递方式,适合用于从方法中传出一个或多个值,尤其适用于 Try 模式和性能敏感的场景。
776

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



