网站首页 > 技术文章 正文
简介
在 C# 中,in、ref 和 out 是用于修改方法参数传递方式的关键字,它们决定了参数是按值传递还是按引用传递,以及参数是否必须在传递前初始化。
基本语义对比
修饰符 | 传递方式 | 可读写性 | 必须初始化 | 调用前必须赋值 | 典型场景 |
ref | 引用传递 | 可读可写 | 需先在调用前初始化 | 是 | 修改调用者变量;传大对象避免拷贝 |
in | 只读引用传递 | 只读(不能赋值) | 需先在调用前初始化 | 是 | 传递大值类型以避免拷贝 |
out | 引用传递 | 必须在方法体内赋值 | 调用前可未初始化 | 否 | 返回多个值 |
示例用法
ref:引用传递,可读可写
void Increment(ref int x)
{
x++; // 修改调用者的值
}
int a = 5;
Increment(ref a);
Console.WriteLine(a); // 输出 6
- 调用前:a 必须已被赋值;
- 语义:方法内对参数的任何写操作会直接反映到调用者;
- 适用:需要在方法中“回写”数据,或对大结构体避免复制(如 ref struct)。
out:输出参数,必须在方法内赋值
bool TryParse(string s, out int result)
{
if (int.TryParse(s, out var tmp))
{
result = tmp;
return true;
}
result = 0;
return false;
}
int value;
if (TryParse("123", out value))
Console.WriteLine(value); // 输出 123
- 调用前:value 可未初始化;
- 方法体内:必须为 out 参数赋值(否则编译不通过);
- 语义:专门用于“输出”数据或多返回值场景;
- C# 7+ 支持声明式 out 变量:if (int.TryParse(s, out var result)) ...。
in:只读引用传递
public struct BigStruct
{
public long A, B, C, D;
public long Sum() => A + B + C + D;
}
void PrintSum(in BigStruct bs)
{
// bs = new BigStruct(); // 编译错误:不能修改 in 参数
Console.WriteLine(bs.Sum());
}
var big = new BigStruct { A=1, B=2, C=3, D=4 };
PrintSum(in big);
- 调用前:参数 big 必须初始化;
- 方法内:只能读取(编译器禁止赋值或调用会改变其字段的成员);
- 语义:避免对大值类型做整拷贝,同时保证安全只读;
- 性能:适用于 16 字节以上的值类型以减少栈/寄存器拷贝开销;
- 限制:不能与可变成员、属性 setter、ref 局部变量一起使用。
最佳实践
优先使用返回值
// 避免使用 out
(bool success, int result) = TryParseBetter("123");
大型结构体使用 in
void ProcessLargeData(in BigStruct data) { ... }
ref 用于需要修改原始值的情况
void UpdatePosition(ref Vector3 position) { ... }
out 用于需要返回多个值的场景
bool TryGetValue(string key, out object value)
避免引用类型使用 in/ref
// 通常不需要 - 引用类型已经通过引用传递
void ProcessList(ref List<int> items) { ... }
差异对比
内存与性能
- 传值(无修饰符)会复制值类型(大对象拷贝成本高);
- ref/out/in 都传地址,避免复制;
- in 保证只读,编译器可做额外优化。
重载差异
可以同时定义带不同修饰符的方法:
void Foo(int x) { }
void Foo(ref int x) { }
void Foo(in int x) { }
void Foo(out int x) { x = 0; }
调用时必须明确修饰符:Foo(a), Foo(ref a), Foo(in a), Foo(out a)
泛型约束
C# 7.3+ 支持泛型中使用 in/ref 修饰符:
void Process<T>(in T item) where T : unmanaged { … }
C# 复制 全屏
总结
- ref:双向修改,需初始化,适合状态更新。
- out:输出参数,无需初始化,方法必须赋值,适合返回多个值。
- in:只读引用,需初始化,适合大结构体性能优化。
猜你喜欢
- 2025-07-24 Channels: C# 实现高效的线程间通信
- 2025-07-24 一路踩坑,被迫聊聊 C# 代码调试技巧和远程调试
- 2025-07-24 C# 获取Windows的系统信息(c# 获取本机的ip地址)
- 2025-07-24 C# 读取本地网络配置信息(c# 读取配置文件的方法)
- 2025-07-24 MODBUS协议在C#中的应用案例(c#modbus 程序)
- 2025-07-24 C#接口(c接口镜头)
- 2025-07-24 AngleSharp :在 C# 中轻松解析和操作 HTML/XML 文档
- 2025-07-24 C#中的9个“黑魔法”与“骚操作”
- 2025-07-24 C#.NET Ninject 详解(c#.net framework)
- 2025-07-24 C#.NET HttpClient 使用教程(c#开启http服务)
- 1517℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 594℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 521℃MySQL service启动脚本浅析(r12笔记第59天)
- 489℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 489℃启用MySQL查询缓存(mysql8.0查询缓存)
- 477℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 456℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 454℃MySQL server PID file could not be found!失败
- 最近发表
-
- PS所有滤镜的说明(六)(ps滤镜详解)
- 5款小白也能用的在线图片编辑器!电商效率飙升就靠它!
- Java变量(java变量有什么作用)
- Java面试常见问题:Java注解(java中的面试题)
- Java编程入门第一课:HelloWorld(java编程从入门到实践)
- Java基础教程:Java继承概述(java里继承的概述)
- java基础之——访问修饰符(private/default/protected/public)
- 如何规划一个合理的JAVA项目工程结构
- 将机器指令翻译成 JavaScript -- 终极目标
- Web 服务器基准测试:Go vs. Node.js vs. Nim vs. Bun
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)