一、按值参数传递
默认情况下C#中的参数默认按值传递,这是常用的方式。这意味着在将参数值传递给方法时将创建一份参数值的副本
- 示例一
static void Foo (int p)
{
p = p + 1; // +1
Console.WriteLine ("p is " + p);
}
static void Main()
{
int x = 8;
Foo (x); // 创建副本
Console.WriteLine ("x is " + x); // x is 8
}
- 示例二
由于Foo (sb);是引用的一份副本,因此将他赋值为null不会把sb也赋值为null
static void Foo (StringBuilder fooSB)
{
fooSB.Append ("test");
fooSB = null;
}
static void Main()
{
StringBuilder sb = new StringBuilder();
Foo (sb);
Console.WriteLine (sb.ToString()); // test
}
二、ref 修饰符
若按引用传递参数则应使用ref 参数修饰符。
- 示例一
由于p和x指向同一块内存地址,给P赋值将改变x的值。ref 修饰符在声明和调用时都是必须的,这样可以清楚的表明程序如何执行
static void Foo (ref int p)
{
p = p + 1; // Increment p by 1
Console.WriteLine (p); // Write p to screen
}
static void Main()
{
int x = 8;
Foo (ref x); // Ask Foo to deal directly with x
Console.WriteLine (x); // x is now 9
}
- 示例二
ref修饰符对于实现交换方法是必要的
static void Swap (ref string a, ref string b)
{
string temp = a;
a = b;
b = temp;
}
static void Main()
{
string x = "Penn";
string y = "Teller";
Swap (ref x, ref y);
Console.WriteLine (x); // Teller
Console.WriteLine (y); // Penn
}
三、out 修饰符
out参数和ref参数类似
- 无需在传入参数之前进行赋值
- 必须在函数结束之前赋值
out 修饰符通常用于获得方法的多个返回值
- 示例一
与ref参数一样out参数按引用传递
static void Split (string name, out string firstNames, out string lastName)
{
int i = name.LastIndexOf (' ');
firstNames = name.Substring (0, i);
lastName = name.Substring (i + 1);
}
static void Main()
{
string a, b;
Split ("Stevie Ray Vaughn", out a, out b);
Console.WriteLine (a); // Stevie Ray
Console.WriteLine (b); // Vaughn
}
out变量及丢弃变量
从C#7.0开始允许在调用含有out参数的方法时直接声明变量
因此示例一种Main方法可做如下简化
- 示例二
static void Main()
{
Split ("Stevie Ray Vaughan", out string a, out string b);
Console.WriteLine (a); // Stevie Ray
Console.WriteLine (b); // Vaughan
}
当调用含有多个out参数的方法时,若并不关注所有参数的值,那么可以用下划线来丢弃那些不感兴趣的参数
处于兼容性考虑如果作用域内已经有一个名为[_]的变量那么丢弃符号失效
Split ("Stevie Ray Vaughan", out string x, out _); // Discard the 2nd param
Console.WriteLine (x);
按引用传递的含义
按引用传递参数是为现存变量的存储位置起一个别名而不是创建一个新的存储位置。
public class Test
{
static int x;
static void Main()
{
Foo(out x);
}
static void Foo(out int y){
Console.WriteLine (x);
y = 10;
Console.WriteLine (x);
}
}
四、in 修饰符
in参数和ref参数类似,但是前者的参数值无法在方法内修改(修改会产生编译时错误)
这个修饰符非常适合向方法传递大型值类型对象,因此编译时不仅可以避免在参数传递时对参数进行拷贝操作而造成开销,还可以保护参数的原始值不会被修改。
in修饰符是重载的一个重要部分
void Main()
{
SomeBigStruct x = default;
Foo (x); // Calls the first overload
Foo (in x); // Calls the second overload
Bar (x); // OK (calls the 'in' overload)
Bar (in x); // OK (calls the 'in' overload)
}
void Foo (SomeBigStruct a) => "Foo".Dump();
void Foo (in SomeBigStruct a) => "in Foo".Dump();
void Bar (in SomeBigStruct a) => "in Bar".Dump();
struct SomeBigStruct
{
public decimal A, B, C, D, E, F, G;
}
五、params 修饰符
该修饰符只能修饰方法的最后一个参数,使方法接收任意数量的指定类型的参数。参数类型必须声明为数组
static int Sum (params int[] ints)
{
int sum = 0;
for (int i = 0; i < ints.Length; i++)
sum += ints[i]; // Increase sum by ints[i]
return sum;
}
static void Main()
{
int total = Sum (1, 2, 3, 4);
Console.WriteLine (total); // 10
// The call to Sum above is equivalent to:
int total2 = Sum (new int[] { 1, 2, 3, 4 } );
}
可选参数
参数声明中提供默认值,这个参数就是可选参数
static void Foo (int x = 23) { Console.WriteLine (x); }
static void Main()
{
Foo(); // 23
Foo (23); // 23 (equivalent to above call)
}
命名参数
除了用位置确定参数,还可以用名称来确定参数
命名参数能够以任意顺序出现
static void Foo (int x, int y) { Console.WriteLine (x + ", " + y); }
static void Main()
{
Foo (x:1, y:2); // 1, 2
Foo (y:2, x:1); // 1, 2 (semantically same as above)
// You can mix named and positional arguments:
Foo (1, y:2);
}
网友评论