• C#基础-ref、out


    1.默认情况下,C#假定所有的方法参数传递都是传值的。

    如下面的方法:

    public static void Main(string[] args)
     {
         int val = 5;
    
         //调用AddValue方法,aVal会重新拷贝一份val的值(即aVal为val的一个实例副本),方法内部的操作并不会改变val的值。
         AddValue(val);
    
         //val值还是5,并没有加1
         Console.WriteLine(val);
         Console.ReadLine();
     }
    
     public static void AddValue(int aVal)
     {
         aVal = aVal + 1;
     }
     
    如果期望在调用AddValue方法以后,val加一,有两种方案:
    1、修改AddValue,将修改后的值作为返回值,并赋值给val。
    2、使用ref关键字、out关键字。传入参数时,不再传入一个原来类型的拷贝,而是直接传入原有值类型的地址(类似于引用类型) ,这样在方法内部的任意修改都会影响到传入的值类型。
     
    2.引用类型的ref、out
    引用类型作为方法参数传入时,对象的引用(或者指向该对象的指针)会传入方法参数,在方法内部对该对象的修改,被调用者也能直接看到。
    这样是不是就不需要ref、out关键字了呢?
    eg:
    其实,引用类型作为方法参数传入时,理论上还是会新建一个变量,该变量指向和原有引用类型的指向相同。
     
    如果在调用方法内部修改了引用类型的属性,使用ref和不使用ref没有区别,参考代码为:
    public static void Main(string[] args)
    {
        User user = new User(); user.Name = "Lisa";
    
        //不使用关键字ref,在调用方法内部修改User对象的属性,也会影响传入参数User.
        //因为此时的User和方法参数aUser都指向同一份引用。
        ChangeUser(user);
        Console.WriteLine(user.Name);
    
        //使用关键字ref,在调用方法内部修改User对象的属性,也会影响传入参数User
        //因为此时的User和方法参数aUser是同一个对象。
        ChangeUser(ref user);
        Console.WriteLine(user.Name);
    
        Console.ReadLine();
    }
    
    public static void ChangeUser(User aUser)
    {
        aUser.Name = "Alan1";
    }
    
    public static void ChangeUser(ref User aUser)
    {
        aUser.Name = "Alan2";
    }
    输出结果为:
    Alan1
    Alan2
    此时看不出不使用ref和使用ref的区别,同样对在方法内部修改User的Name属性,效果一样
     
     
    如果在调用方法内部修改了引用类型的指向,使用ref和不使用ref有区别,参考代码为:
    public static void Main(string[] args)
    {
        User user = new User();
    
        //不使用关键字ref,在调用方法内部修改User对象的引用,不会影响传入参数User.
        //因为在栈中有两个独立的指针user、aUser
        ChangeUser(user);
        if (user != null)
        {
            Console.WriteLine("user1 is not null");
        }
        else
        {
            Console.WriteLine("user1 is null");
        }
    
        //使用关键字ref,在调用方法内部修改User对象的引用,会影响传入参数User.
        //因为在栈中仅有一个指针user
        ChangeUser(ref user);
        if (user != null)
        {
            Console.WriteLine("user2 is not null");
        }
        else
        {
            Console.WriteLine("user2 is null");
        }
    
        Console.ReadLine();
    }
    
    public static void ChangeUser(User aUser)
    {
        aUser = null;
    }
    
    public static void ChangeUser(ref User aUser)
    {
        aUser = null;
    }
    输出结果为:
    user1 is not null
    user2 is null
    此时能够明细看错不使用ref和使用ref的区别,同样对在方法内部赋值User ==null,效果不一样
     
    引用关系图为:
    引用类型Val本身
    image 
     
    调用Change(aVal)方法以后:
    image
     
    调用Change(ref aVal)方法以后:
    image 
    从上可知,如果需要真正的指向同一个引用,在方法内部的任何改变都会影响到传入参数,还是需要使用ref和out关键字。
     
    3.从CLR来说,ref和out本身是一样的,都导致传递指向实例的指针。
    唯一的区别是:
    ref参数需要调用者在调用方法之前初始化参数的值(强制要求,不赋值编译不通过)
    out参数不指望调用者在调用方法之前初始化参数默认值,即使调用者之前赋值,在调用方法内部也会重新赋值(即调用者的赋值会被替换,赋值无效,所以不建议调用者提前赋值)。
     
    4.其它
    a.允许对方法进行ref和out进行重载(即认为使用ref/out跟不使用ref/out函数签名不一致)
    以下方法能够编译通过。

    public static void ChangeUser(User aUser)
    {
        aUser = null;
    }

    public static void ChangeUser(ref User aUser)
    {
        aUser = null;
    }

    编译通过

    但是,如果两个方法只有ref和out的区别,是不允许的。因为两个方法签名的元数据是完全相同的。
    以下方法编译不过

    public static void ChangeUser(ref User aUser)
    {
        aUser = null;
    }

    public static void ChangeUser(out User aUser)
    {
        aUser = null;
    }

    编译不过:

    错误: “ChangeUser”不能定义仅在 ref 和 out 上有差别的重载方法  

    b.使用ref/out关键字时,方法定义和调用时参数类型必须一致。

     
     
  • 相关阅读:
    【分享】浅析Quora的技术架构 狼人:
    【观点】在苹果公司学到的编程技巧 狼人:
    【观点】工作效率上的错觉 狼人:
    提高编程技巧的十大方法 狼人:
    微软推出IE10第二个平台预览版 狼人:
    读取文件将 Excel 文件 转换成 CSV 文件 解决方案
    组合生成组合的生成之生成下一个组合 By ACReaper
    二叉树遍历二叉树的实现及先序、中序、后序遍历
    图片切换[置顶] 送大家几款可以运用到实际项目的flash+xml控件
    动画效果程序员的最高境界就是能够参加全球DEMO大赛
  • 原文地址:https://www.cnblogs.com/liaozh/p/3463197.html
Copyright © 2020-2023  润新知