博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ref和out,以及一般方法的引用参数和值参数寻解
阅读量:6484 次
发布时间:2019-06-23

本文共 4222 字,大约阅读时间需要 14 分钟。

——老赵微博

 对与ref和out的区别,我相信很多人都知道,这里我简单罗列下:

1.首先ref和out两种类型的参数都是可以将方法内对参数的修改传递到方法外面

2.ref参数需要在传递之前初始化,out不需要,out参数在返回时必须赋值

3.在CLR角度看ref和out没什么区别,但是C#编译器采取不同的方式对待

下面通过一些实例来进一步了解

①一般方法传参

namespace ConsoleApplication1{    class Program    {        static void Main(string[] args)        {            TestParas tp = new TestParas();            Int32 a = 300;            String b = "wan";            String ret = tp.Add(a, b);            Console.WriteLine(ret);        }    }    public class TestParas    {        public String Add(Int32 a, String b)        {            return a.ToString() + b;        }    }}

上面是非常普通,常见的传参。查看IL:

.method private hidebysig static     void Main (        string[] args    ) cil managed {    .entrypoint    .locals init (        [0] class ConsoleApplication1.TestParas tp,        [1] int32 a,        [2] string b,        [3] string 'ret'    )    IL_0000: nop     //下面两行定义了一个tp的引用类型的实例,这里创建一个对象,并把对象的引用入栈    IL_0001: newobj instance void ConsoleApplication1.TestParas::.ctor()    IL_0006: stloc.0 //这里会有一个出栈,把出栈的值存在本地变量里,也就是这里的tp    IL_0007: ldc.i4 300 //这里定义一个值类型,先将提供的值入栈    IL_000c: stloc.1    //出栈,将值存在本地变量a中    IL_000d: ldstr "wan" //这里定义一个引用类型,将对该字符串的引用存入元素据    IL_0012: stloc.2  //取出当前栈的值放到本地变量b中    IL_0013: ldloc.0    IL_0014: ldloc.1    IL_0015: ldloc.2    IL_0016: callvirt instance string ConsoleApplication1.TestParas::Add(int32,  string)//这里调用add方法,并传入两个参数    IL_001b: stloc.3    IL_001c: ldloc.3    IL_001d: call void [mscorlib]System.Console::WriteLine(string)    IL_0022: nop    IL_0023: ret}

接下来看看add方法的IL:

下面改变代码,传入带ref和out的:

View Code
namespace ConsoleApplication1{    class Program    {        static void Main(string[] args)        {            TestParas tp = new TestParas();            Int32 a = 300;            String b = "wan";            String ret = tp.Add(ref a, ref b);            Console.WriteLine(ret);        }    }    public class TestParas    {        public String Add(ref Int32 a, ref String b)        {            return a.ToString() + b;        }    }}

这时对应的IL也发生了变化,变化的部分如图:

这里跟C语言里面的一种传参方式相同,传递的是地址。接着看下Add方法的IL:

这里可以看到,在Add方法里定义了两个地址,分别对应传入的a,b的地址。所以在这里操作是针对a,b的地址操作,当然也就能将参数的变化传递到方法外。

为了效果明显,可以使用一个交换两个数的方法来展示:

View Code
namespace ConsoleApplication1{    class Program    {        static void Main(string[] args)        {            TestParas tp = new TestParas();            Int32 a = 300;            Int32 c = 1000;            Console.WriteLine("交换a:{0}和c:{1}",a,c);            tp.Change2(a, c);            Console.WriteLine("没有加ref执行交换,a:{0},c{1}", a, c);            tp.Change(ref a, ref c);                      Console.WriteLine("加上ref交换后a:{0},c:{1}",a,c);        }    }    public class TestParas    {                public void Change(ref Int32 a, ref Int32 c)        {            Int32 tmp = 0;            tmp = a;            a = c;            c = tmp;        }        public void Change2(Int32 a, Int32 c)        {            Int32 tmp = 0;            tmp = a;            a = c;            c = tmp;        }    }}

运行结果为:

最后,给个结论吧:老赵的说法正确

ps:在默认情况下,CLR假定所有的方法参数传递的仅仅是值。当然除了加了ref和out以外。

今天看到有不少朋友在评论给了不少这方面的理解,还有一些扩展的例子,非常感谢,对我很有帮助。我觉得还可以挖掘一下,看下面的例子:

namespace ConsoleApplication1{    class Program    {        static void Main(string[] args)        {            Person person = new Person();            person.Name = "初始值";            person.Age = 24;            Int32 a = 24;            ProcessPerson proc = new ProcessPerson();            Console.WriteLine("姓名:" + person.Name + "  年龄:" + person.Age + "  a:" + a);            proc.Change(person,ref a);            Console.WriteLine("姓名:" + person.Name + "  年龄:" + person.Age + "  a:" + a);        }    }    public class Person    {        public String Name { get; set; }        public Int32 Age { get; set; }    }    public class ProcessPerson    {        public void Change(Person obj,ref Int32 a)        {            obj.Name = "被修改";            obj.Age = 25;            a = 25;        }    }}

首先说明下,这里Change方法的Person obj参数,加不加ref都会返回被修改的值。这里的obj是一个引用类型,里面包括Name,Age属性分别是引用类型和值类型。这里对它们的修改是传递到了方法外的。而对应的值类型a参数,则会在加了ref后发送改变。

所以这里我推测:首先,CLR默认对所有参数传递是值传递,所以这里应该成立,obj是一个引用类型。这里传递的应该obj包含的值(这里的值包含String类型的Name和Int32类型Age)。如果对obj的Name和Age修改,则在返回后在Main方法里面,打印出来的是修改之后的Name和Age的值。这是我的理解,还请路过的朋友多提自己的看法。

转载地址:http://meiuo.baihongyu.com/

你可能感兴趣的文章
python (3):wxPython打包app,报错
查看>>
给网站更换服务器需要注意什么?
查看>>
成长型企业ERP系统实施的八大准则
查看>>
nginx重启脚本
查看>>
理解Linux系统/etc/init.d目录和/etc/rc.local脚本
查看>>
代码整洁之道
查看>>
svm 预测标签的概率输出
查看>>
ActiveMQ(25):优化与建议
查看>>
使用Intelij Idea经过的坑
查看>>
微信 token
查看>>
【原创】JAVA通过过滤器防止脚本注入
查看>>
马哥linux第8周作业
查看>>
gnu autotools
查看>>
在AIX上增加文件系统空间
查看>>
svchost cpu占用率过高电脑卡死
查看>>
【中小企业经典案例分析一】基础架构描述
查看>>
Android进程间通信(IPC)机制Binder简要介绍和学习计划
查看>>
在git@osc上托管自己的代码
查看>>
软件架构师的职责范围谈
查看>>
计算思维与创新创业 课程 获批
查看>>