欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C#中参数为引用类型加ref和不加的区别

程序员文章站 2022-04-30 13:37:41
...

首先说说两种数据类型:值类型和引用类型的区别

值类型

包含int、double、char、bool、struct、还有枚举enum,
声明一个值类型的变量会在栈上分块空间用来存储变量的值,如int a= 1,a的值直接存储在栈上

引用类型

包含类、string、object,Interface,引用类型的对象或实例存储在堆上,而栈上存储的是指向这个堆的地址如ClassA s= new ClassA;画了个简单的示意图
C#中参数为引用类型加ref和不加的区别

ref的作用

好了,下面来说说引用类型的参数加ref和不加的区别,其实从上面可以知道,引用类型的参数传递传的都不是对象本身,而是地址。不加ref关键字,我们可以看成是把对象赋值给了另外一个地址,然后把这个地址作为参数传递到方法中。如下面这段代码

 static main()
        {
            ClassA a = new ClassA();
            Method(a);
        }
        public void Method(ClassA b)
        {

        }

在调用Method(a)时,可以理解为将a赋值给了b,即ClassA b = a,这下就好理解了吧,就是在栈上分配了个新的地址,然后指向了a,这就相当于a、b地址不同,但都指向同一个对象。那么在Method中堆b的所有更改都会反应到a上。我用下面的代码做了个验证

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace MyTest02
{
    class Program
    {
        static void Main(string[] args)
        {
            UserInfo info = new UserInfo();
            info.UserName = "wenjl";
            string addr1 = GetMemory(info);
            Console.WriteLine("main 中 info 的地址:{0}", addr1);
            Console.WriteLine("调用方法之前的哈希code:{0}",info.GetHashCode());
            string dearNm = GetDearName(info);
            int code = info.GetHashCode();
            Console.WriteLine("main 中哈希code:{0}",code);
            Console.ReadLine();
        }

        /// <summary>
        /// 获取昵称
        /// </summary>
        /// <param name="info"></param>
        public static string GetDearName(UserInfo info)
        {
            string dearName = "Bad Boy";
            if ("wenjl".Equals(info.UserName))
            {
                dearName = "Good Boy";
            }
            info.DearName = dearName;
            string addr2 = GetMemory(info);
            Console.WriteLine("GetDearName 中 info 的地址:{0}", addr2);
            int code = info.GetHashCode();
            Console.WriteLine("GetDearName 中哈希code:{0}", code);
            return dearName;
        }

        public static string GetMemory(object o)
        {
            GCHandle h = GCHandle.Alloc(o, GCHandleType.WeakTrackResurrection);
            IntPtr addr = GCHandle.ToIntPtr(h);
            return "0x" + addr.ToString("X");
        }
    }
}

运行结果

C#中参数为引用类型加ref和不加的区别
从运行结果中可看出,main方法和GetDearName方法中info的地址是不一样的,但是哈希code是一致的,证明了上面的说法。
再看下面的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace MyTest02
{
    class Program
    {
        static void Main(string[] args)
        {
            UserInfo info = new UserInfo();
            info.UserName = "wenjl";
            string addr1 = GetMemory(info);
            Console.WriteLine("main 中 info 的地址:{0}", addr1);
            Console.WriteLine("调用方法之前的哈希code:{0}",info.GetHashCode());
            string dearNm = GetDearName(info);
            int code = info.GetHashCode();
            Console.WriteLine("main 中哈希code:{0}",code);
            Console.ReadLine();
        }

        /// <summary>
        /// 获取昵称
        /// </summary>
        /// <param name="info"></param>
        public static string GetDearName(UserInfo info)
        {
            Console.WriteLine("GetDearName中 赋值之前的哈希code:{0}", info.GetHashCode());
            UserInfo user = new UserInfo()
            {
                DearName = "Bad Boy",
                UserName = "wenjl"
            };
            string dearName = "Bad Boy";
            if ("wenjl".Equals(info.UserName))
            {
                dearName = "Good Boy";
            }
            info = user;
            string addr2 = GetMemory(info);
            Console.WriteLine("GetDearName 中 info 的地址:{0}", addr2);
            int code = info.GetHashCode();
            Console.WriteLine("GetDearName 中哈希code:{0}", code);
            return dearName;
        }

        public static string GetMemory(object o)
        {
            GCHandle h = GCHandle.Alloc(o, GCHandleType.WeakTrackResurrection);
            IntPtr addr = GCHandle.ToIntPtr(h);
            return "0x" + addr.ToString("X");
        }
    }
}

C#中参数为引用类型加ref和不加的区别
通过运行结果,看以看出,当把另一个对象赋值给info时,main中的HashCode和GetDearName中info的HashCode不一致了,GetDearName中的info指向了另外一个对象,这将不满足把GetDearName中info的变化反应到Main中的info上了。这也就是ref的作用。

在GetDearName中的参数前面显示加上ref关键字,则表示传递的参数就是main中info的地址,没有重新分配栈地址。这样就算GetDearName中执行info=user,main中的info也会指向user这个对象,但是地址没有发生变化,把上面的GetDearName方法的参数前加上ref关键字,main调用中也加上,结果如下图

C#中参数为引用类型加ref和不加的区别

Mark下,如有不对之处,欢迎指出。