本文已参与「新人创作礼亅活动,一起开启掘金创作之路。
关于在c#方法中参数修饰符ref、out、param、in的用法和区别
ref
一般情况下,方法参数如果是引用类型,则在方法体中对其值的修改会影响方法体外参数的值,但是对于参数是值类型的,则不会;如果有修改的需要(比如参数在方法中需要计算更新),则可以使用ref修饰符
private void AddOne(int _a)
{
_a += 1;
}
private void AddOneRef(ref int _a)
{
_a += 1;
}
int a = 0;
Console.WriteLine($"a1={a}");
AddOne(a);
Console.WriteLine($"a2={a}");
AddOneRef(ref a);
Console.WriteLine($"a3={a}");
/*Output
a1=0
a2=0
a3=2
*/
从代码中可以看到,AddOne方法只是普通值类型参数,因此将 _a的值修改了,也不会影响到a,但是AddOneRef有了ref修饰符,则a也 +1变成2了.
ref将值类型参数按引用传递,而非值传递,可以理解成将a的地址传进方法中,这点在IL代码就有体现出来,下图是将那2个方法转成IL代码,可以看出AddOne 参数是 int32 _a,值传递会copy出一个新变量,即使修改也影响不到原变量;而AddOneRef 参数是int32& _a,是传地址,因此对_a进行修改会影响a,他们其实就是同一个变量。
out
out修饰符跟ref相似,ref要求在传递变量之前已经初始化,并且不要求在方法中对变量进行修改。而out要求在方法中必须对变量进行赋值
private void Set(out int _a)
{
//不对_a进行修改会编译错误 [CS0177] The out parameter '_a' must be assigned to before control leaves the current method
_a = 3;
}
int a = 0;
AddOneOut
Console.WriteLine($"a1={a}"); //a = 3
下图是out的IL代码,可见也是传地址
in
in修饰符与ref、out类似,都是将参数改成引用传递,并且要求参数在传递给方法之前必须已经初始化,该修饰符的特点是禁止了参数在方法中的修改,
public static void Main(string[] args)
{
int a = 0;
AddOneIn(a);
Console.WriteLine(a);
}
static void AddOneIn(in int _a)
{
_a += 1; //Cannot assign to variable 'in int' because it is a readonly variable
}
那么in修饰符有什么用呢,我们知道如果传递的参数是一个值类型比如struct,在传递时复制成本会比较高,如果使用in则可以改成引用传递减少复制变量这部分成本
params
一般的当一个方法被调用时,会希望调用者传入与方法相同类型和数量的参数,除非存在有默认值参数(不传递该参数则使用默认值)。param修饰符可以为方法指定参数数量可变的特性,传递方法的参数由编译器更改为临时数组中的元素。下面两种写法都可以
为什么不直接传递一个数组给AddAllNum方法,而是给方法参数加params呢?我的理解是,当方法在不同的调用场景下,参数数量、类型都不一样,不同参数类型之间如果形成一个数组的在业务上会难以理解,而且当有params修饰符时,可以一个参数都不传
void UseParams(params object[] list){}
UseParams(); //可以不传递参数
UseParams(1,"小明",16);//方法里根据list下标处理好对应数据类型即可
params修饰符可以无需创建重载方法
int AddAllNum(int a, int b){}
int AddAllNum(int a, int b,int c){}
int AddAllNum(int a, int b, int c, int d){}
当使用了params时,在该参数之后不需要有其他任何类型的参数