C#值传递、引用传递、输出传递

通过前面的学习我们知道,在调用带有参数的函数时,需要将参数传递给函数。在介绍这几种传递方式之前,我们先来介绍一下形式参数(形参)和实际参数(实参)这两个概念:
  • 形式参数:在定义函数阶段参数列表中定义的参数称之为形式参数,简称形参,可以将它看作变量的名称,它没有具体的值,只是用来接收函数调用时传递过来的数据;
  • 实际参数:在函数被调用时传递给函数的参数称之为实际参数,简称实参,可以将它看作变量的值,用来为形参赋值。

参数可以通过三种方式传递给函数,如下表所示:

方式 描述
值传递 值传递会复制参数的实际值并赋值给函数的形式参数,实参和形参使用的是两个不同内存位置中的值,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全
引用传递 引用传递会复制参数的内存位置并传递给形式参数,当形参的值发生改变时,同时也会改变实参的值
输出传递 输出传递可以一次返回多个值

1、值传递

C# 中,值传递是将参数传递给函数的默认方式,值传递的本质就是将实参的副本(将实参的值复制一份)传递给函数的形参。当调用函数时,将使用实参为每个形参赋值,并为每个形参创建一个新的存储位置,由于形参和实参指向不同的内存位置,所以无论是修改实参的值还是修改形参的值都不会对彼此造成影响。

下面通过示例来演示一下如何使用值传递的方式将参数传递给函数:
using System;

namespace c.biancheng.net
{
    class Demo
    {
        static void Main(string[] args){
            int val = 10;
            Demo Obj = new Demo();
            Console.WriteLine("调用函数之前:{0}", val);
            Obj.Func(val);
            Console.WriteLine("调用函数之后:{0}", val);
        }
        public void Func(int val){
            val *= val;
            Console.WriteLine("函数内部的值:{0}", val);
        }
    }
}
运行结果如下:

调用函数之前:10
函数内部的值:100
调用函数之后:10

通过运行结果可以看出,尽管我们在函数内部对形参 val 的值进行的修改,但是并不会影响函数外面实参 val 的值。

2、引用传递

引用传递是对变量内存位置的引用。与值传递不同,使用引用传递的形式传递参数时,并不会为形参创建新的内存地址,而是与实参共同指向相同的内存地址。正因为如此,当修改形参的值时,实参的值也会被修改。

在 C# 中,需要使用 ref 关键字来使用引用传递,下面通过示例来演示一下:
using System;

namespace c.biancheng.net
{
    class Demo
    {
        static void Main(string[] args){
            int val = 10;
            Demo Obj = new Demo();
            Console.WriteLine("调用函数之前:{0}", val);
            Obj.Func(ref val);
            Console.WriteLine("调用函数之后:{0}", val);
        }
        public void Func(ref int val){
            val *= val;
            Console.WriteLine("函数内部的值:{0}", val);
        }
    }
}
运行结果如下:

调用函数之前:10
函数内部的值:100
调用函数之后:100

3、输出传递

使用 return 语句可以从函数中返回一个值,但是使用输出传递则可以从函数中一次性返回多个值。输出传递与引用传递相似,不同之处在于输出传递是将数据从函数中传输出来而不是传输到函数中。

在 C# 中,需要使用 out 关键字来使用输出传递,下面通过示例来演示一下:
using System;

namespace c.biancheng.net
{
    class Demo
    {
        static void Main(string[] args){
            int val = 33;
            Demo Obj = new Demo();
            Console.WriteLine("调用函数之前 val 的值:{0}", val);
            Obj.getValue(out val);
            Console.WriteLine("调用函数之后 val 的值:{0}", val);
        }
        public void getValue(out int x){
            int temp = 11;
            x = temp;
            x *= x;
        }
    }
}
运行结果如下:

调用函数之前 val 的值:33
调用函数之后 val 的值:121

在使用输出传递时,也可以不为实参赋值,如下例所示:
using System;

namespace c.biancheng.net
{
    class Demo
    {
        static void Main(string[] args){
            int a, b;
            Demo Obj = new Demo();
            Obj.getValues(out a, out b);
            Console.WriteLine("调用函数之后 a 的值:{0}", a);
            Console.WriteLine("调用函数之后 b 的值:{0}", b);
        }
        public void getValues(out int x, out int y){
            Console.WriteLine("请输入第一个值: ");
            x = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine("请输入第二个值: ");
            y = Convert.ToInt32(Console.ReadLine());
        }
    }
}
运行结果如下:

请输入第一个值:
123
请输入第二个值:
321
调用函数之后 a 的值:123
调用函数之后 b 的值:321