C# ref 关键字详解:值类型与引用类型的传参差异

Felix
Felix
发布时间:2026-10-15 | 4人浏览过
原创

C# 中的 ref 关键字用于按引用传递参数,使形参成为实参的别名。这不仅解决了值类型无法修改外部变量的限制,也在引用类型需要更新指针指向全新对象实例时发挥关键作用。

在 C# 编程中,方法的参数默认是按值传递的。微软官方文档指出,当在参数列表中使用 ref 关键字时,参数将按引用传递而非按值传递。这意味着形参不再是值的副本,而是实参的直接别名,任何对形参的操作都会直接反映到实参上。

要深入理解这一机制,首先需明确内存中“值”与“引用”的区别:

  • 值类型:如 int, double, bool, struct 等。它们的数据内容直接存储在栈(Stack)内存中。
  • 引用类型:如 class, interface, delegate, string 等。其实例数据存储在托管堆(Heap)中,而栈中仅存储一个指向堆内存地址的引用(指针)。

为什么值类型入参通常需要 ref?

这是最常见的应用场景。当你希望在一个方法中修改一个外部传入的基本类型变量(如整数或布尔值)时,如果不加 ref,方法接收到的只是该变量值的一个副本。方法内的修改只会影响这个副本,原变量保持不变。

加上 ref 后,传递的是栈中具体数值的“引用”。这使得方法内部可以直接操作栈上的原始数据,从而改变实参的值。

引用类型加 ref 有什么作用?

既然引用类型在栈中已经存有一个指向堆对象的地址,为什么有时还需要加 ref?这取决于你在方法内部想要执行的操作是“修改属性”还是“替换对象”。

场景一:修改属性(无需 ref)

如果你只是想改变对象内部的某个字段或属性,即使不加 ref,修改也会生效。因为此时方法拿到的是那个“指向堆的地址”,顺着地址去改里面的内容是没问题的。

Student student1 = new Student("Jack");
WithoutRef(student1);
Console.WriteLine($"结果 --- {student1.Name}"); // 输出: Bruce

static void WithoutRef(Student stu)
{
    stu.Name = "Bruce"; // 修改堆内存中的数据
}

public class Student
{
    public string Name { get; set; }
    public Student(string name) { Name = name; }
}

场景二:替换实例对象(必须 ref)

如果在方法内部调用了 new 关键字创建了新对象,情况就变了:

Student student2 = new Student("Lucy");
WithRef(ref student2);
Console.WriteLine($"带ref的结果 --- {student2.Name}"); // 输出: Bruce

static void WithRef(ref Student stu)
{
    stu = new Student("Bruce"); // 尝试改变栈里的地址指向
}

public class Student
{
    public string Name { get; set; }
    public Student(string name) { Name = name; }
}

分析如下:

  1. 不带 ref 的情况:方法接收到的是实参地址的一个副本(比如地址 `0x123`)。当执行 stu = new... 时,它在栈里开辟了一块新区域存了另一个对象的地址(比如 `0x456`),但这并没有影响到外面实参存储的 `0x123`。外面的变量依然指向旧对象。
  2. 带 ref 的情况:传递的是实参变量所在的栈地址本身。当执行赋值操作时,直接覆盖了实参栈里的值,将其指向了新创建的堆对象。因此外部变量也随之更新了。

总结

  • ref 传递的是变量的栈地址。
  • 对于值类型,不加 ref 无法改变外部变量的值;加了则可以。
  • 对于引用类型,仅修改属性不需要 ref;但要在方法内部彻底更换引用的对象实例(即改变栈中存的地址),必须加 ref。

以上就是对 C# 参数传递机制及 ref 关键字用法的详细内容,更多关于 C# 引用类型传参与性能优化的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表