zoukankan      html  css  js  c++  java
  • ref和out,以及一般方法的引用参数和值参数寻解

     对与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

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

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

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

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

    View Code

    运行结果为:

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

    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的值。这是我的理解,还请路过的朋友多提自己的看法。

  • 相关阅读:
    docker~save与load的使用
    docker~从Dockerfile到Container的过程(终于算是OK了)
    docker~使用阿里加速器
    Draw2d中的布局管理器Layout比较
    利用glibc中锁结构的信息解决死锁问题
    android 利用重力感应监听 来电时翻转手机后静音。
    hdu 1754 I Hate It
    九度笔记之 1209最小邮票数
    java zip工具类
    基于XMPP实现的Openfire的配置安装+Android客户端的实现
  • 原文地址:https://www.cnblogs.com/zwb7926/p/3139665.html
Copyright © 2011-2022 走看看