C# ref out parase 理解

这节课我们来学习方法中的参数传递,在面向对象二中,我曾说过,参数也属于变量的一种,在c语言的学习时,同学们都学习过参数这个概念和用法,方法使用参数列表来传递变量的基本语法如下:
returnType  FunctionName(paraType1  paraName1,paraType2  paraName2,……)
{
   Function body;
}
其中的returnType是指方法的返回值,FunctionName指方法的标识符,paraType1指参数1的数据类型,paraName1是参数1的标识符,调用这种类型的方法的时候,必须传入与类型paraType1相符的参数值,并且传入的参数个数也必须相符,方法内部以参数名称paraName1来识别。
     在C#中,参数一共四种形式:分别是

参数类型值参数
引用参数--ref
输出参数--out
动态参数--parmas

他们都有什么区别呢?具体怎么用呢?我们现在就开始具体的讲解,
值参数



值参数就是参数的是值类型的,如 int   add(int i,int j){},i和j就是值参数,值参数很好理解,我们也经常用到,下面的代码实例主要是为了对比引用参数和输出参数的区别的实现两个数值交换的例子,请同学们体会它们的不同。


 1//定义一个交换两个数的值的一个方法Exchange
 2        //此处请注意,本方法使用了static关键字,原因是:根据我们类一所提到的,如果是实例方法(不加static关键字),
 3        //必须通过类的对象来调用,即使在同一个类中,加入static关键字后,静态成员就属于类的引用范围了,
 4        //因为在一个类中,所以不必类名.,在其他方法中直接就可以调用这个方法。对比下面的实例方法
 5        static void Exchange(int a, int b)
 6        {
 7            Console .WriteLine ();
 8            Console.WriteLine("值参数方法进来时,a={0}  b={1}", a, b);
 9            int c = 0;
10            c = a;
11            a = b;
12            b = c;
13            Console.WriteLine();
14            Console.WriteLine("经过值参数交换方法交换后,a={0}  b={1}", a, b);
15            Console.WriteLine();
16
17        }
18        //看这个实例方法,对照上一个方法,看在main方法中的调用是不同的,
19        //此方法必须通过Class2的对象s.来调用,而上一个方法是直接调用(因为在一个类中,省略了类名.来调用)
20        void Exchange1()
21        { 
22        }
23
24        static void Main()
25        {
26            //输入初值
27            Console.Write("请输入整数a=");
28            int a = int.Parse(Console .ReadLine());
29            //输入初值
30            Console.Write("请输入整数b=");
31            int b = int.Parse(Console.ReadLine());
32            
33            //观察1:经过交换后a和b的结果的变化,(方法结束时交换了a和b的值)
34            //2:对比下面的调用,记住静态方法调用的方式
35            //3:在main方法结束前,我们再次打印main中定义的a和b的值的变化(和初值相同)
36            Exchange(a,b);
37
38
39            //对比上面的调用,记住实例方法调用的方式
40            Class2 s = new Class2();
41            s.Exchange1();
42
43            //因为是值参数的传递方法,不会改变Main方法中的a的值,结果应该和初值相同
44            Console.WriteLine("采用值参数交换方法后,Main方法中现在为a="+a);
45            Console.WriteLine();
46            //因为是值参数的传递方法,不会改变Main方法中的b的值,结果应该和初值相同
47            Console.WriteLine("经过值参数交换方法后,Main方法中现在为b=" + b);
48        }
49
50       
51    }

运行结果

请输入整数a=2
请输入整数b=4

值参数方法进来时,a=2  b=4

经过值参数交换方法交换后,a=4  b=2

采用值参数交换方法后,Main方法中现在为a=2

经过值参数交换方法后,Main方法中现在为b=4
请按任意键继续. . .

可以看出值参数经过方法传递后,初值是不改变的,下面我们来看一下引用参数。

引用参数



如果方法的参数为数值类型时,则传递的方式主要分为两种:传值和传址。这两种方式最大的差异在于:当一个参数以传值的方式传递时,变量的值即使在方法中被改变,它本身还是维持一开始传入的值,我们刚才学习的值参数就是传值方式;而以传址方式传入的变量,当方法将其改变的时候,此变量的值便永远被更改,C#默认以传值方式传递参数,如果要使用传址方式传递,就需要用到下面我要说的理论。数值类型一律是以传值方式进行传递的,如果要改变这种行为,用传址方式传递参数,必须使用ref和out关键字进行修饰,当然如果参数本身就是引用类型的(string类型除外),不需要加ref和out关键字,他们的传递也是采用传址方式的.

使用ref关键字修饰的引用参数传递的方法书写格式如:static  int add(ref int i,ref int j){}  .在参数数值类型的前面加入ref修饰,在调用这个方法时,传入的参数前也必须加ref关键字,如:add(ref a,ref b);

下面我们上面的例子中Exchange方法中参数b变成ref形式,同学们来体会一下a作为值参数传值传递和b作为引用参数传址传递的区别:


 1class Class2
 2    {
 3        //定义一个交换两个数的值的一个方法Exchange
 4        //此处请注意,本方法使用了static关键字,原因是:根据我们类一所提到的,如果是实例方法(不加static关键字),
 5        //必须通过类的对象来调用,即使在同一个类中,加入static关键字后,静态成员就属于类的引用范围了,
 6        //因为在一个类中,所以不必类名.,在其他方法中直接就可以调用这个方法。对比下面的实例方法
 7        static void Exchange(int a, ref int b)
 8        {
 9            Console .WriteLine ();
10            Console.WriteLine("参数进来时,a={0}  b={1}", a, b);
11            int c = 0;
12            c = a;
13            a = b;
14            b = c;
15            Console.WriteLine();
16            Console.WriteLine("方法交换后,a={0}  b={1}", a, b);
17            Console.WriteLine();
18
19        }
20        //看这个实例方法,对照上一个方法,看在main方法中的调用是不同的,
21        //此方法必须通过Class2的对象s.来调用,而上一个方法是直接调用(因为在一个类中,省略了类名.来调用)
22        void Exchange1()
23        { 
24        }
25
26        static void Main()
27        {
28            //输入初值
29            Console.Write("请输入整数a=");
30            int a = int.Parse(Console .ReadLine());
31            //输入初值
32            Console.Write("请输入整数b=");
33            int b = int.Parse(Console.ReadLine());
34            
35            //观察1:经过交换后a和b的结果的变化,(方法结束时交换了a和b的值)
36            //2:对比下面的调用,记住静态方法调用的方式
37            //3:main方法结束前,我们再次打印main中定义的a和b的值的变化:a和初值相同,b与出方法值相同
38            Exchange(a,ref b);
39
40
41            //对比上面的调用,记住实例方法调用的方式
42            Class2 s = new Class2();
43            s.Exchange1();
44
45            //因为是值参数的传递方法,不会改变Main方法中的a的值,结果应该和初值相同
46            Console.WriteLine("采用值参数传递交换方法后,结果应该和初值相同,Main方法中现在为a=" + a);
47            Console.WriteLine();
48            //因为是引用ref参数的传递方法,会改变Main方法中的b的值,结果应该和出方法时的值相同
49            Console.WriteLine("经过引用参数传递交换方法后,结果应该和出方法时的值相同,Main方法中现在为b=" + b);
50        }   
51    }

运行结果

请输入整数a=2
请输入整数b=4

参数进来时,a=2  b=4

方法交换后,a=4  b=2

采用值参数传递交换方法后,结果应该和初值相同,Main方法中现在为a=2

经过引用参数传递交换方法后,结果应该和出方法时的值相同,Main方法中现在为b=2
请按任意键继续. . .

很明显看出,Main方法中b的值变化了,和出方法时的值相同,而a因为是传值传递,仍然是初值。参数是数值型的传址传递还有一种就是使用out关键字,那么它与ref有什么不同呢?

输出参数



输出参数传址时,要使用out关键值,out关键字允许我们以一个未初始化的变量作为参数,直接传入方法当中,在方法结束的时候,指定所要返回的值给它,而使用ref关键字传给方法之前,必须将一个数值传入到方法中,out关键字在某种程度上来说,有点像return的功能,就是把方法的结果返回到调用这个方法的主方法中,使用out关键字要注意到:主方法中传入调用的方法的out参数值应该为未初始化的,即使你初始化了,初始化的值也相当于不存在。我们来看这个例子,体会一下out的用法。


 1class Class2
 2    {
 3        //定义一个求正方形面积的方法,传入正方形的边长,返回正方形的面积
 4        static void QMJ(double bc,out  double mj)
 5          {
 6              mj = bc * bc;       
 7           }
 8
 9        //对比使用return返回面积的方法,达到的效果是一样的
10        static double returnQMJ(double bc)
11        {
12           return bc * bc;
13        }
14        static void Main()
15        {
16            Console.Write("请输入正方形的边长bc=");
17            double bc = double.Parse(Console.ReadLine());
18            Console.WriteLine();
19            //传入QMJ方法的mj是为赋初值的
20            double mj;
21
22            //通过方法调用,改变mj的值为出方法的值,打印出来,bc的值不变
23            QMJ(bc, out mj);            
24            Console.WriteLine("正方形的面积mj="+mj);
25
26            Console.WriteLine("=================");
27            //使用return方式,返回方法的结果值
28            Console.WriteLine("使用returnQMJ方法面积mj="+returnQMJ(bc));
29        }   
30    }
31

运行结果

请输入正方形的边长bc=4

正方形的面积mj=16
=================
使用returnQMJ方法面积mj=16
请按任意键继续. . .

这个例子我主要是想告诉大家使用out能够达到和return返回值一样的功能效果。

下面我们来看一下为什么加了ref和out关键字后,主方法中的数值会改变呢?这主要是因为存放参数的内存机制发生了变化而引起的。上面的3个例子都是数值类型作为参数,但是因为第二、三例子中加入了ref和out关键字,改变了数值类型按值传递的特性,改为按照传递在内存中存放的地址,按地址传递后参数的值会改变的原因就在此,因为按内存地址传递时,调用方法的参数在定义时,不会因为参数是数值类型而在线程堆栈中开辟新的空间来存放新的值,而是会在线程堆栈中存放一个指向主方法定义的变量(初值)的内存地址,当这个地址指向的内存值因为调用方法中对参数的作用(如交换,如平方)后,内存中的值就改成了调用方法作用后的值,这样在打印主方法的变量时,值自然就改成了调用方法后的值了。

接下来我们来看一个参数为引用类型的实例,因为参数本身就是引用类型(string类型除外),所以这种参数传递属于传址传递:

数组为参数的传址传递

运行结果:

数值参数传入时,数组中的每个元素值是:
1  2  3
改变后,方法内部的数组a中的每个元素值是:
2  4  6
经过方法后,Main方法中数组a的每个元素值是:
2  4  6  请按任意键继续. . .

通过这个实例,同学们应该看出引用类型作为参数时,也会影响主方法中带入到调用方法的值。下面我们来学习最后一直参数类型叫做动态传参。

动态传参



动态传参主要是指参数的个数可以根据需要改变,使用动态传参要用到parmas关键字,它的具体用法我们来看这个例子:

动态传参实例1

对照一下上个例子和本例,我们会发现,动态传参,不必规定数组的个数,而是根据需要使用,本例结果如下:

数值参数传入时,数组中的每个元素值是:
1  2  3  4
改变后,方法内部的数组a中的每个元素值是:
2  4  6  8
请按任意键继续. . .

现在我们来做下一个例子,就是调用方法中有两个参数,一个int型,一个动态的数组类型parmas int[],要告诉大家的是,如果使用parma动态参数,动态参数一定只有一个,必须当有多个参数时,动态参数的位置在最后,我们把上面的例子修改一下,大家体会一下不同。

动态传参实例2

运行结果

数值参数传入时,数组中的每个元素值是:
2  3  4  k=1

改变后,方法内部的数组a中的每个元素值是:
4  6  8  k=1

请按任意键继续. . .

可以看出因为把parmas的动态参数放到了最后,编译器就自动的把第一个值1直接给了k,把剩下的2,3,4给了数值a参数带入到方法中,所以使用parmas一定要注意到这个问题。

时间: 2024-10-14 08:02:31

C# ref out parase 理解的相关文章

如何理解Java中参数传递只能传值?

以前学习C#的时候,是完全在工作岗位上学习,一些底层较为深入的道理都不是很清楚.如今学习了Java,对于Java参数传递只能传值,不能传引用(指针)感到很困惑,在C#中不是常常说把某个引用传递到函数中吗?甚至C#有相当简便的ref.out参数,明明白白的传引用. 经过一番探索,得出的结论表明,Java中我不管你到底是传值还是传引用,只需要记住原生数据类型(值类型)和String作为参数传递的时候,其原本的值都不会发生改变:而引用类型在作为参数传递时,函数中对其的操作,都会反馈到引用所指向的值.

MySQL Explain 结果解读与实践

Explain 结果解读与实践 基于 MySQL 5.0.67 ,存储引擎 MyISAM . 注:单独一行的"%%"及"`"表示分隔内容,就象分开"第一章""第二章". explain 可以分析 select 语句的执行,即 MySQL 的"执行计划": mysql> explain select 1; +----+-------------+-------+------+--------------

详解MySQL中EXPLAIN解释命令

Explain 结果解读与实践 基于 MySQL 5.0.67 ,存储引擎 MyISAM . 注:单独一行的"%%"及"`"表示分隔内容,就象分开“第一章”“第二章”. explain 可以分析 select 语句的执行,即 MySQL 的“执行计划”: mysql> explain select 1; +----+-------------+-------+------+---------------+------+---------+------+----

Perl入门(六) Perl方法的使用

 1.定义一个方法 Perl使用sub定义方法. 语法: sub 方法名称{方法体} 2.调用一个方法 Perl直接使用方法名称调用方法. 调用方式有以下四种: 方法名称: &方法名称: 方法名称(); &方法名称(); 说明:方法调用可以再任何位置,可以在方法前.后调用,也可以在方法体内部调用. 3.传递参数 Perl通过方法名后面的括号将参数列表传递到方法体内.例如:function_name("param1","param2"...); 方

关于c#中”ref”和”out”关键字的一些理解

一. 综述(本文内容大部分来自网络,经本人整理而成,仅供学习参考,不免理解错误,欢迎批评指正) 在c#中,方法的参数传递有四种类型: (1) 传值参数(by value) 传值参数无需额外的修饰符.传值参数在方法调用过程中,如果改变了参数的值,那么传入方法的参数在方法调用完成以后并不因此而改变,而是保持原来传入的值.实际 上,传值参数传递的是调用参数的一份拷贝,因此在调用方法的过程中,即使改变了参数的值,也不会影响到实际传入的参数值,详见例程: (2) 传址参数(by reference) 传址

c#基础系列3---深入理解ref 和out

"大菜":源于自己刚踏入猿途混沌时起,自我感觉不是一般的菜,因而得名"大菜",于自身共勉. 扩展阅读 c#基础系列1---深入理解 值类型和引用类型 c#基础系列2---深入理解 String 在上篇文章深入理解值类型和引用类型的时候,有的小伙伴就推荐说一说ref和out 关键字,昨天晚上彻夜难眠在想是否要谈一下呢,因为可谈的不是太多,也可能是我理解的不够深刻. 应用场景 out 修饰函数参数,以传递引用的方式向函数传递参数. out 关键字也可与泛型类型参数结合使

由于对vue生命周期理解的不透彻引发的ref属性的问题

之前 对vue的生命周期 是对于口头上的理解(没有应用于实战) 而今天写了个demo 关于ref属性应用的问题 在此重新理解下vue的生命周期 vue的生命周期就是vue的实例对象从创建到销毁的过程 1.创建前(beforeCreate): 实例初始化后,数据观察和时间机制都没形成,不能获取到Dom的节点. 2.创建后(created):在这个阶段,vue实例已经创建,仍然不能获取Dom. 3.载入前(beforeMount):在这一阶段,依然不能获取Dom元素,但是vue挂载的根节点已经创建,

深入理解javascript的作用域--函数声明为什么会前置

标签: javascript函数对象 这篇博文解决了以下迷惑 函数声明为什么前置 函数声明前置和变量前置优先级问题 为什么js文件开头就可以使用Math,String等库,而不需要导入头文件 1.变量对象VO 变量对象(Variable Object, 缩写为VO)是一个抽象 概念中的"对象",它用于存储执行上下文中的: 1. 变量 2. 函数声明 3. 函数参数 js解释器就是通过变量对象(VO)来找到我们定义的变量和函数的. 举个例子: var a = 10; function t

理解HTC Vive更新——控制相机旋转和位移

本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/72188658 作者:cartzhang 一.写在前面 在HTC的vive 头盔中, 一旦Vive头盔连接都unity游戏中,就会控制所有Camera的旋转和位置. 这对于有需要的控制非头盔相机带来了烦恼. 比方说,上篇博客中,在VR中,对某个特点位置截图,就会由于头盔控制所有相机的旋转, 造成截图不精确和出现偏移. 地址: