C#参数

一个方法可以定义0个或多个参数。

形参和实参

方法定义时使用的参数是形参,形参在方法中使用时相当于局部变量。

方法调用时传递的参数时实参,实参通过栈以副本的形式传递给形参。

对于值类型,副本是值本身的副本,对于引用类型,是引用的副本。

值类型传值

static void Swap(int a,int b){
    int t = a;
    a=b;
    b=t;
}
static void Main(string[] args){
    int x=10;
    int y=20;
       Swap(x,y);
       Console.WriteLine("x={0},y={1}",x,y);//x=10,y=20
}

Main函数中的局部变量x,y以副本的形式传递给实参Swap方法的形参a,bSwap方法交换了ab的值,但是由于交换的是副本,不能影响到Main函数的x,y

引用类型传值,修改引用指向

static void Swap(int[] a){
    int t = a[0];
    a[0]=a[1];
    a[1]=t;
}
static void Main(string[] args){
    int[] x = new int[10,20];
       Swap(x);
       Console.WriteLine("x[0]={0},x[1]={1}",x[0],[x1]);//x[0]=20,x[1]=10
}

Main函数中的局部变量x以副本的形式传递给实参Swap方法的形参aSwap方法交换了a引用元素的两个值的值,由于xa指向同一个引用,可以影响到Main函数。

引用类型传值,修改引用自身

static void Swap(int[] a){
    a = new int[a[1],a[0]];
}
static void Main(string[] args){
    int[] x = new int[10,20];
       Swap(x);
       Console.WriteLine("x[0]={0},x[1]={1}",x[0],[x1]);//x[0]=10,x[1]=20
}

Main函数中的局部变量x以副本的形式传递给实参Swap方法的形参aSwap方法将a的引用指向了新数组,此时ax指向同不同的引用,引用x指向的值没有被修改,不影响Main函数。

ref参数

在参数类型加上ref修饰,说明参数是传引用的。ref参数调用是参数,调用后相当于返回值。

对于值类型,传递的时值的引用(的副本);对于引用类型,是引用的引用(的副本)。

ref参数在调用时需要初始化,需要在实参加上ref前缀。

static void Swap(ref int a,ref int b){
    int t = a;
    a=b;
    b=t;
}
static void Main(string[] args){
    int x=10;
    int y=20;
       Swap(ref x,ref y);
       Console.WriteLine("x={0},y={1}",x,y);//x=20,y=10
}

out参数

在参数类型上加上out修饰符,说明参数是传引用的,相当于返回值。

out参数在函数内部必须初始化。

out参数在调用时无需初始化,需要加上实参前加out。


static void GetValue(out int value){
    value = 10;
}

static void Main(string[] args){
    int x;
    GetValue(out x);//x=10
}

在C#6.0以后,out实数可以临时定义。

static void GetValue(out int value){
    value = 10;
}
static void Main(string[] args){
    GetValue(out int x);//x=10
}

临时定义的out实参=变量的作用域是当前块。

注意:不能定义仅仅由out和ref区分的重载。

in参数 (C#7.2)

在参数类型上加上in修饰符,说明参数是传引用的,但只作为参数使用,不能修改参数的值。

in参数主要是为了高效传递结构体。


struct LargeStrut
{
    public int x;
    public int y;
    public int y
}

void F(in LargeStruct o)
{
    //这里不能修改o的指向,也不能修改o的成员
}

值类型和引用类型的传值和传引用

下表说明不同传参方式、不同类型如何通过参数对调用者造成影响

类型 传值 传引用
值类型 无法影响 修改引用指向
引用类型 修改引用状态 修改引用状态,修改引用指向

值类型传值

传递的是值的副本,修改参数无法影响到调用者。

static int Swap(int a,int b)
{
    //交换的变量a、b都是值的副本,无法影响调用者
    int t=a;
    a=b;
    b=t;
}

static void Main(string[] args){
    int x=10;
    int y=20;
       Swap(x,y);
       Console.WriteLine("x={0},y={1}",x,y);//x=10,y=20
}

值类型传引用

传递的是值的引用(地址)(的副本),修改参数可以影响到调用者。

static void Swap(ref int a,ref int b)
{
    //交换的变量a、b传递的是值的引用(地址)(的副本),传递的是值的地址,可以影响调用者
    int t=a;
    a=b;
    b=t;
}

static void Main(string[] args){
    int x=10;
    int y=20;
       Swap(ref x,ref y);
       Console.WriteLine("x={0},y={1}",x,y);//x=20,y=10
}

引用类型传值

传递的是引用(地址)(的副本),若通过引用影响了对象状态,可以影响到调用者,修改引用自身(副本)无法影响调用者。

引用类型传值,修改引用无法影响调用者。

为测试引用类型的传值和传引用,定义一个引用类型,存储一个整数。

public class Integer{
   public Integer(int value){
       this.value=value;
   }
   public int value;
   public override string ToString(){
       return value.ToString();
   }
}
static void Swap(Integer a,Integer b)
{
    //变量a、b都是按值传递的引用类型,是引用(地址)的副本

    //修改引用(副本)不能影响调用者
    Integer t=a;
    a=b;
    b=t;
}

static void Main(string[] args){
    Integer x = new Integer(10);
    Integer y = new Integer(20);
       Swap(x,y);
       Console.WriteLine("x={0},y={1}",x,y);//x=10,y=20
}

引用类型传值,变更引用对象状态可以影响调用者。

static int Swap(Integer a,Integer b)
{
    //变量a、b都是按值传递的引用类型,是引用(地址)的副本

    //变更对象的状态可以影响调用者
    int t= a.value;
    a.value=b.value;
    b.value=t;
}

static void Main(string[] args){
    Integer x = new Integer(10);
    Integer y = new Integer(20);
       Swap(x,y);
       Console.WriteLine("x={0},y={1}",x,y);//x=20,y=10
}

引用类型传引用

传递的是引用(地址)的引用(地址)(的副本),若通过引用影响了对象状态,可以影响到调用者;通过修改引用,也可以影响调用者。

引用类型传引用,变更引用对象状态可以影响调用者。

static void Swap(ref Integer a,ref Integer b)
{
    //变量a、b都是按引用传递的引用类型,是引用(地址)的地址(的副本)

    //变更对象的状态也可以影响调用者
    int t= a.value;
    a.value=b.value;
    b.value=t;
}

static void Main(string[] args){
    Integer x = new Integer(10);
    Integer y = new Integer(20);
       Swap(ref x,ref y);
       Console.WriteLine("x={0},y={1}",x,y);//x=20,y=10
}

引用类型传引用,修改引用可以影响调用者。

static void Swap(ref Integer a,ref Integer b)
{
    //变量a、b都是按引用传递的引用类型,是引用(地址)的地址(的副本)

    //修改引用也可以影响调用者
    Integer t=a;
    a=b;
    b=t;
}

static void Main(string[] args){
    Integer x = new Integer(10);
    Integer y = new Integer(20);
       Swap(ref x,ref y);
       Console.WriteLine("x={0},y={1}",x,y);//x=20,y=10
}

传递方式总结

对于值类型,为了修改能影响到调用者,要使用传引用的方式。

对于引用类型,传值的方式就能通过修改对象状态来影响调用者。

不定参数

在最后一个数组类型的参数前上加上params修饰符,说明参数数量是可变的。

static int Sum(params int[] values){
    int sum=0;
    foreach(int value in values){
        sum+=value;
    }
    return sum;
}

调用

Sum();//0 0元素数组
Sum(1,2);//3
Sum(1,2,3,4);//10
Sum(new int[]{4,5,6});//25 自己创建数组

可选参数(C#4.0)

可以为参数设置默认值。

定义带默认值的参数

static void F(int i = 100, string s = "ok"){

}

调用带默认值的参数。

F();//两个参数都保持默认值
F(10);//第二个参数使用默认值

命名参数(C#4.0)

在调用方法时,可以通过参数名:表达式来传递参数。

F(s:"hi");//第1个参数使用默认值,只对第2个参数进行赋值。