c#-函数的参数传递

一、概述

        C#的参数传递是一个不大不小的问题,一旦掌握不清楚,很容易出现一些低级错误,排查起来相当麻烦。这里就将参数传递的一些问题说明一下,组织目录如下:

1.c#参数分类

2.传递参数的几种方式及对参数的影响

二、主要内容

1.参数分类

通用系统类型(common type system  CTS)主要分为两大类:值类型引用类型。结构图如下:


从图中,我们可以知道:CTS类型不是值类型,就是引用类型

        c#对这两种类型数据的存储是不一样的:对于值类型,将直接存储在托管栈中(stack),对于引用类型,只将对数据的引用(地址)存在托管栈中,真实的数据存在于托管堆(heap)。如图所示:

分配在托管栈中的变量,会在创建它们的方法结束时,自动释放。而分配在托管堆中的变量,并不会在创建它们的方法结束时释放内存,而是有垃圾回收机制在将来的某个时候去释放内存。上面标红的这句话是我们分析后面代码的基础,请好好理解。

2.参数传递的几种方式

传递参数有按值传递和按引用传递之分,这样,和数据类型简单的组合一下,便得到以下4种传递方式:

(1)按值传递值类型。

(2)按引用传递值类型。

(3)按值传递引用类型。

(4)按引用传递引用类型。

一般来说,除非使用特定的关键字(ref和out。关于它们的微小差别,可以参考后面给出的第二个链接),否则参数是按值传递的,也就是说,会传递一个副本。

下面根据代码来分析每种方式对参数的影响:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace test
{
   
    class Program
    {
        #region<span style="font-family: Arial, Helvetica, sans-serif;">//点类</span>
        class point
        {
            //属性
            int X{get;set;}
            int Y{get;set;}
            //构造函数
            public point(int x,int y)
            {
                X = x;
                Y = y;
            }
            //设置坐标
            public void set(int x,int y)
            {
                X = x;
                Y = y;
            }
           //输出点
            public void output()
            {
                
                Console.WriteLine("("+X.ToString()+","+Y.ToString()+")");
            }
        }
        #endregion
       //按值传递值类型数据
        static void value_transmit_value(int i)
        {
            i = 3;
        }
        //按引用传递值类型数据
        static void refer_transmit_value(ref int i)
        {
            i = 3;
        }
        //按值传递引用类型数据
        static void value_transmit_refer(point pt)
        {
            pt.set(5, 5);
        }
        //按引用传递引用类型数据
        static void refer_transmit_refer(ref point pt)
        {
            pt.set(5, 5);
        }
        //按值传递引用类型数据,并改变参数指向
        static void newpt(point pt)
        {
            point ptnew = new point(9, 9);
            pt = ptnew;
        }
        //按引用传递引用类型数据,并改变参数指向
        static void newpt(ref point pt)
        {
            point ptnew = new point(9, 9);
            pt = ptnew;
        }
        #region 测试代码
        static void Main(string[] args)
        {
           //1.
            int number = 0;
            value_transmit_value(number);
            Console.WriteLine(number);//按值传递并未改变其值
            refer_transmit_value(ref number);//按引用传递改变了值
            Console.WriteLine(number);
            
            point pt = new point(0,0); 
            //对于引用类型不管是按值传递还是按引用传递都改变了参数值
            value_transmit_refer(pt);
            pt.output();
            refer_transmit_refer(ref pt);
            pt.output();

            //2.对于赋值,不管是按值传递还是按引用传递都改变了参数和原始对象的值。
            point pt_1 = pt;
            value_transmit_refer(pt_1);
            pt_1.output();
            pt.output();
            refer_transmit_refer(ref pt_1);
            pt_1.output();
            pt.output();

            //3.按值传递没有改变参数值
            newpt(pt);
            pt.output();
            //4.按引用传递改变了参数值
            newpt(ref pt);
            pt.output();

            //5.对于赋值,按值传递没有改变了参数和原始对象的值。
            point pt_2 = pt;
            newpt(pt_2);
            pt_2.output();
            pt.output();

            //6.对于赋值,按引用传递改变了参数值,没有改变原始对象的值。
            point pt_3 = pt;
            newpt(ref pt_3);
            pt_3.output();
            pt.output();           
        }
        #endregion
    }
}
运行结果:

1.

2.


3.


4.


5.


6.



对参数的影响:

1.测试代码里有一个point类,可以通过set方法改变坐标值。然后有一系列方法测试传递参数值。

2.对于值类型的参数,很简单,按值传递不会改变参数值(因为传递的是副本),按引用传递会改变参数值。

3.对于引用类型的参数,对于修改参数本身,不管是按值传递还是按引用类型传递,都会改变参数值。(因为最终都会根据地址修改托管堆里的数据);

4.当方法内部开辟了新的对象,并使参数引用它时,按值传递并没有改变参数值,按引用传递却改变了参数值,见结果3和结果4.(因为按值传递给的是副本,并不会改变参数内容(地址),所以存储的值也就不会改变。按引用传递改变了其内容(地址),使其指向了新的对象,所以值改变 了。原来的对象因为没有引用,会被垃圾回收机制回收。)

5.如果参数本身是一个引用(在方法外部引用),当方法内部开辟了新的对象,并使参数引用它时,按值传递没有改变参数值和原始对象值,按引用传递改变了参数值,没有改变原始对象值,见结果5和结果6.(原理同上,按值传递不会改变其内容(地址)。按引用传递改变了地址,但也仅仅是该参数指向的地址改变了,不再指向原始对象的数据,所以参数值改变了,但原始对象不会改变。(因为其地址未改变))

另外通过结果3,4,5,6可以知道,不管何种传递都不会影响到原始对象的值,只可能影响到引用它的参数的值。这也从侧面验证了上面的分析。3,4,5的分析很冗长,其实只需要知道两点就可以了:1.按值传递的,传递的是参数的副本,按引用传递,可以直接操作参数。2.值类型,将直接存储在托管栈中(stack),对于引用类型,只将对数据的引用(地址)存在托管栈中,真实的数据存在于托管堆(heap)。根据这两点就可以很轻松的掌握参数传递规律,希望对你有所帮助: )。

参考:
1.《c#4.0权威指南》
2.http://blog.youkuaiyun.com/weiwenhp/article/details/7644790
3.http://www.cnblogs.com/DonLiang/archive/2008/02/16/1070717.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值