C#传参方式
- 一、默认传递
- [二、`ref` 引用传递(双向通信)](#二、
ref引用传递(双向通信)) - [三、`out` 输出参数(只出不进)](#三、
out输出参数(只出不进)) - [四、`in` 只读传递(性能优化利器)](#四、
in只读传递(性能优化利器)) - [五、`params` 可变参数](#五、
params可变参数)
| 传递方式 | 关键字 | 底层机制 | 值类型表现 | 引用类型表现 | 典型使用场景 |
|---|---|---|---|---|---|
| 默认值传递 | 无 | 复制一份数据(值类型复制内容,引用类型复制内存地址) | 方法内修改不影响外部 | 方法内修改属性会影响外部;但重新 new 不影响外部 |
绝大多数常规方法调用 |
| 引用传递 | ref |
传递变量的真实内存地址 | 方法内修改直接影响外部 | 方法内修改属性或重新 new 都直接影响外部 |
需要方法修改外部传入的变量(需提前初始化),必须加 ref |
| 输出参数 | out |
传递变量的真实内存地址 | 方法内必须重新赋值,外部才能获取新值 | 方法内必须重新赋值,外部才能获取新对象 | 方法需要返回多个结果(不要求传入前初始化),必须加 out |
| 只读引用 | in |
传递内存地址,但加上只读保护 | 方法内不可修改,避免大结构体复制开销 | 方法内不可修改引用,不可重新 new |
传递大型 struct 时的性能优化(必须初始化),可加 in,也可省略 |
| 可变参数 | params |
编译器自动将多个参数打包为数组 | 接收任意数量的同类型参数 | 接收任意数量的同类型参数 | 类似 string.Format() 的灵活传参 |
一、默认传递
引用类型传递的是地址的副本。这意味着你可以用这个副本去修改原对象的属性,但你不能把原对象"换掉"
c#
public class Person { public string? Name { get; set; } }
public static void DefaultTest(Person p, int num)
{
p.Name = "李四"; // 修改属性:外部原对象会被修改!
num = 999; // 修改值类型:外部原变量不受影响
p = new Person { Name = "测试" }; // 重新赋值:外部原对象不受影响!
}
static void Main(string[] args)
{
Person p = new Person();
p.Name = "张三";
int num = 10;
Console.WriteLine($"修改前: name:{p.Name}, num:{num}");
DefaultTest(p, num);
Console.WriteLine($"修改后: name:{p.Name}, num:{num}");
}
二、ref 引用传递(双向通信)
使用
ref时,形参和实参指向完全相同的内存,任何操作都会同步到外部
c#
public class Person { public string? Name { get; set; } }
public static void RefTest(ref Person p, ref int num)
{
num = 999; // 外部变量直接变成 999
p = new Person { Name = "李四" }; // 外部变量直接指向新对象
// p.Name = "李四"; // 修改属性:外部原对象会被修改!
}
static void Main(string[] args)
{
Person p = new Person();
p.Name = "张三";
int num = 10;
Console.WriteLine($"修改前: name:{p.Name}, num:{num}");
RefTest(ref p, ref num);
Console.WriteLine($"修改后: name:{p.Name}, num:{num}");
}
三、out 输出参数(只出不进)
out适合用来返回多个结果,传入前不需要初始化,但方法内部必须赋值
c#
static void Main(string[] args)
{
int a;
if (int.TryParse("10", out a))
{
Console.WriteLine($"a:{a}");
}
int b;
if (int.TryParse("xyz", out b))
{
Console.WriteLine($"b:{b}");
}
else
{
Console.WriteLine($"b:{b}");
}
}
四、in 只读传递(性能优化利器)
主要用于大型结构体(
struct),避免复制带来的内存开销,同时保证数据安全
c#
public struct Point
{
public int X; public int Y; public int Z;
}
public static void InTest(in Point point)
{
//point = new Point(); // ❌ 编译报错:不能修改
//point.X = 10; // ❌ 编译报错:不能修改属性
var copy = point; // ✅ 可以读取
}
五、params 可变参数
必须放在参数列表的最后
c#
static void Main(string[] args)
{
Console.WriteLine("{0},{1},{2},{3}", "hello", "world", "hello", "world");
}