C#中参数为引用类型加ref和不加的区别
首先说说两种数据类型:值类型和引用类型的区别
值类型
包含int、double、char、bool、struct、还有枚举enum,
声明一个值类型的变量会在栈上分块空间用来存储变量的值,如int a= 1,a的值直接存储在栈上
引用类型
包含类、string、object,Interface,引用类型的对象或实例存储在堆上,而栈上存储的是指向这个堆的地址如ClassA s= new ClassA;画了个简单的示意图
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");
}
}
}
运行结果
从运行结果中可看出,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");
}
}
}
通过运行结果,看以看出,当把另一个对象赋值给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调用中也加上,结果如下图
Mark下,如有不对之处,欢迎指出。