C#参数传递小结

时间:2022-03-06 04:45:46

  

  在C#中方法的参数可以通过两种方式传递:值方式传递、引用方式传递

  通过引用方式传递参数,允许函数成员更改参数的值并保持该更改。若要通过引用方式传递,则需使用关键字ref或out。在C#中除非特别说明,否则都是以值方式传递数据。值类型变量直接包含其数据;引用类型变量不直接包含其数据,它包含的是对数据的引用。因此按值方式传递变量意味着向方法传递变量的一个副本,按引用方式传递变量意味着向方法传递变量的引用。

  根据参数类型和传递方式的不同,有以下4种情况:

  1. 值类型参数按值方式传递
  2. 引用类型参数按值方式传递
  3. 值类型参数按引用方式传递
  4. 引用类型参数按引用方式传递

  *按引用传递可以用ref修饰,也可以用out修饰,具体区别后面会做介绍。

  下面将分别进行说明:

  1.值类型参数按值方式传递

  值类型参数传递的是该值类型的一个拷贝,被调用方法操作的是属于自己本身的拷贝,因此不影响原来调用方法中的参数值。见如下Demo:

static void Main(string[] args)
{
  int num1 = 10;
  Add(num1);
  Console.WriteLine(num1);
}
static
void Add(int num1) {   num1 = num1 + 5;   Console.Write(num1 + "\t"); }

  以上Demo运行的结果为:15  10,可以看出Main()方法中num1的值只传递了一个参数的拷贝,在Add()方法中的改变并没有影响到Main()方法中num1的值,其值仍为10。

  2.引用类型参数按值方式传递

  

static void Main(string[] args)
{
    int[] num = { 1, 3, 5 };
    Console.WriteLine("num[0]={0}",num[0]);
    ChangeTest(num);
    Console.WriteLine("num[0]={0}",num[0]);
    Console.WriteLine("num[1]={0}",num[1]);
}
static void ChangeTest(int[] x)
{
    x[0] = -5;
    x = new int[] { 2, 4, 6 };
    Console.WriteLine("x[0]={0}",x[0]);
}

  输出结果为:

  num[0]=1

  x[0]=2

  num[0]=-5

  num[1]=3

  变量num为引用类型,由于采用的是值方式传递,所以将向方法传递指向num的引用的副本。由于num和x 都指向了同一个对象,所以对值的修改会保存,但在方法内部,使用new运算符重新对x分配内存,将使变量x引用新的数组,在这之后所做的更改将不会影响原数组num。

  按值方式传递的实质的是传递值,不同的是这个值在值类型和引用类型的表现是不同的:参数为值类型时,“值”为数据本身,因此传递的是数据拷贝,不会对原来的数据产生影响;参数为引用类型时,“值”为对象引用,因此传递的是引用地址拷贝(指向同一个对象),这是二者在统一概念上的表现区别,理解了本质也就抓住了根源。

  3.值类型参数按引用方式传递 

  不管是值类型还是引用类型,按引用传递必须以ref或者out关键字来修饰,其规则是:方法定义和方法调用必须同时显示的使用ref或者out,否则将导致编译错误。见如下Demo:

  static void Main(string[] args)

  {

    int num = 5;

    Console.WriteLine("num={0}",num);

    ChangeTest(ref num);

    Console.WriteLine("num={0}",num);

  }

  static void ChangeTest(ref int x)

  {

    x *= x;

    Console.WriteLine("x={0}",x);

  }

  输出结果为:

  num=5

  x=25

  num=25

  传递的不是num的值,而是num的引用,参数x不是int类型,它是对int(即num)的引用,所以在方法内对x求平方时,实际被求平方的是x所引用的项。

  如果仅仅是按引用传递值类型参数的话,那么不会发生装箱和拆箱的问题。也就是说,它并没有产生另外一份数据,而是用指针的方式指向了参数所代表的那份数据而已(这份数据可能在栈上面,也可能在堆上面),但总之是一个指针引用,所以说,按照引用传递的情况,我们如果在ChangeTest中修改了num的值,那么后续访问num这个变量,它的值就确实被改变了。

  4.引用类型参数的按引用传递

  static void Main(string[] args)

  {

    int[] num = { 1, 3, 5 };

    Console.WriteLine("num[0]={0}", num[0]);

    ChangeTest(ref num);

    Console.WriteLine("num[0]={0}", num[0]);

    Console.WriteLine("num[1]={0}", num[1]);

  }

  static void ChangeTest(ref int[] x)

  {

    x[0] = -5;

    x = new int[] { 2, 4, 6 };

    Console.WriteLine("x[0]={0}", x[0]);

  }

  输出结果为:

  num[0]=1

  x[0]=2

  num[0]=2

  num[1]=4

  按引用方式传递时,不管参数是值类型还是引用类型,传递的是参数的地址,也就是实例的指针。ref和out关键字将告诉编译器,方法传递的是参数地址,而不是参数本身。如果参数是引用类型,则按引用传递时,传递的是引用的引用而不是引用本身,类似于指针的指针概念。

  总结ref和out的区别

  在C# 中,既可以通过值方式也可以通过引用方式传递参数。通过引用方式传递参数允许函数成员更改参数的值,并保持该更改。若要通过引用方式传递参数, 可使用ref或out关键字。ref和out这两个关键字都能够提供相似的功效。它们的区别是:

  1.使用ref型参数时,传入的参数必须先被初始化。对out而言,必须在方法中对其完成初始化。

  2.out适合用在需要retrun多个返回值的地方,而ref则用在需要被调用的方法修改调用者的引用的时候。