关于C#中的参数传递问题

来源:互联网 发布:淘宝7了个3 编辑:程序博客网 时间:2024/05/01 20:14
 

作者:老徐       邮箱:xyxtl@163.com    QQ:362880403

 

很多同学反映对于方法调用时的参数传递问题感觉比较困惑,甚至不理解。下面我就谈谈这方面的问题。

首先我们要理解参数传递的作用,为什么要用到参数传递?

如图1所示:

类A中存在成员变量var,两个成员方法:方法1和方法2,方法1中存在局部变量var1,方法2中存在局部变量var2.现在方法1有个任务要完成,因为这个任务比较复杂,所以他要初一部分来让访法2来完成,也就是要调用访法2。访法2要完成的工作就是处理数据,把结果返回给访法1。

现在我们看,如果访法2要处理的数据是类A的成员变量var。因为var和方法1、方法2同为类的成员,他们可以相互访问,也就是说变量为两个方法所共同拥有,这时候是不需要传递变量var的,因为在两个方法中都可以访问到变量var。

如果方法1交给方法2的任务是处理变量var1时情况就不同了,因为变量var1是存在于方法1中的局部变量,只有在方法1的内部才能访问到var1。所以,只有把var1的信息传递给方法2,访法2才能完成任务。这时候就需要也是必须传递参数

我们知道:变量分为值类型引用类型。值类型变量通常分配在栈内存,变量中直接包含数据的副本,所以使用的效率比较高。而引用类型变量分配在托管堆中,引用类型变量中通常包含一个指向实例的指针,变量通过指针引用实例。举个例子来说:把变量比作一个盘子,我们要用他来存放一个苹果。如果把苹果直接放到盘子里,这个盘子就是一个值类型的变量,因为它直接存放的苹果本身;如果盘子里不直接放苹果,而是放了一张纸条,这个纸条上写着苹果所在的具体位置,我们可以在盘子里找到纸条,然后通过纸条上的“苹果的地址”拿到苹果,这时候盘子就是一个引用类型的变量。

这两种类型的变量作为参数被传递时,情况有什么不同呢?

先看值类型变量的传递:

static void Main(string[] args)

        {

            int a = 3, b = 5;

            Console.WriteLine("a={0},b={1}",a,b);

            Swap(a, b);

            Console.WriteLine("a={0},b={1}", a, b);

            Console.ReadKey();

        }

 

static void Swap(int x, int y)

        {

            int z;

            z = x;

            x = y;

            y = z;

    }

如图2:


参数a,向参数x传递,因为a是整型变量,属于值类型。在传递参数时传递的是变量的值,也就是数据副本。这时候系统会首先新开辟一个存储空间x,然后把参数a的值拷贝到x中。参数b向参数y传递亦同。在方法swap中,x,y,z三个变量如图示方向赋值,x和y中的值将交换。但因为a和x,b和y的存储空间是彼此独立的,所以交换x,y的值并不会影响到变量a,b;

所以执行结果为:

 

 

 

 

下面我们看引用类型变量的传递:

class A

    {

        public int num;

    }

 

    class Program

    {

        static void Main(string[] args)

        {

            A a1 = new A();

            A a2 = new A();

            a1.num = 3;

            a2.num = 5;

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

            Swap(a1, a2);

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

            Console.ReadKey();

        }

 

        static void Swap(A x, A y)

        {

            A z;

            z = x;

            x = y;

            y = z;

        }

    }

 

 

 如图3:

变量a1和a2是类A的实例对象,系统在托管堆中分配存储空间a1和a2,其中保存了指向各自实例的指针,把a1和a2作为参数传递,其实传递的还是参数中包含的内容,但这时候两个变量中的内容不是实例本身,而是指向实例的指针,简单的说就是传递的是地址。这样的话x接收a1的内容,也就是a1所指向的实例的地址,y接收的是a2的内容,也就是a2所指向的实例的地址。同图2一样,通过中间变量z,实现了x,y两个变量内容的交换,这里交换的是保存在x,y中的指针,x和y中包含的指向实例的指针交换了,而不是a1和a2的内容,更不是a1和a2对应实例的内容。那这个交换显然没有影响到a1,a2两个变量各自实例的内容。

所以执行结果:

 

 

 

         综合上面两个例子,我们不难看出不管参数是值类型还是引用类型,其实在传递参数时都是首先创建参数的一个副本,然后把参数的内容拷贝到副本中。如果参数是值类型,那传递的内容是数据副本,如果是引用类型,传递的则是对象实例的指针。

C#比较拽的地方是,可以使用ref关键字指定按引用方式传递参数。当控制权传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。

class A

    {

        public int num;

    }

 

    class Program

    {

        static void Main(string[] args)

        {

            A a1 = new A();

            A a2 = new A();

            a1.num = 3;

            a2.num = 5;

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

            Swap(ref a1, ref a2);

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

            Console.ReadKey();

        }

 

        static void Swap(ref A x, ref A y)

        {

            A z;

            z = x;

            x = y;

            y = z;

        }

    }

 

 

 

 

如图4:

  这时候的参数的传递,并不给形参x,y分配存储空间,我们可以理解为给实参a1,a2分别取了一个别名叫x,y.这样,就像有的人有名字还有乳名一样。所以,a1和x其实是一个变量,a2和y也是一个变量。对x,y执行操作,就是对a1,a2执行操作。

所以,显而易见x,y交换值其实就是交换a1,a2的内容,也就是各自实例的指针,指针改变了,他们指向的实例也就变了。

所以,执行结果为:

 

总结:方法调用时何时需要传递参数,需要传递什么参数,需要大家在遇到实际问题时仔细分析,认真思考。参数类型以及是否用ref传递,几种参数传递方式的原理和结果都是不同的,希望大家认真思考,多分析传递的过程,多做练习,多总结经验。

原创粉丝点击