C#值类型与引用类型在使用上的区别
- 值类型与引用类型
为了探明两者区别,直接看代码:
public class Object_1
{
private int m_Age;
public int Age
{
get { return m_Age; }
set { m_Age = value; }
}
private string m_Namr;
public string Name
{
get { return m_Namr; }
set { m_Namr = value; }
}
}
public struct Struct_1
{
private int m_Age;
public int Age
{
get { return m_Age; }
set { m_Age = value; }
}
private string m_Namr;
public string Name
{
get { return m_Namr; }
set { m_Namr = value; }
}
}
在上面我们定义了一个类(Object_1)和一个结构体(Struct_1)。我们都知道类是引用类型,而结构体是值类型,所以接下来进行对比,我们让两者在Change()方法中改变值:
private void Form1_Load(object sender, EventArgs e)
{
Object_1 obj = new Object_1();
obj.Age = 1;
obj.Name = "Mike";
Struct_1 stru = new Struct_1();
stru.Age = 2;
stru.Name = "Tim";
Change(obj,stru);
MessageBox.Show("obj.Age:" + obj.Age + "\nobj.Name:" + obj.Name + "\nstru.Age:" + stru.Age + "\nstru.Name:" + stru.Name);
}
public void Change(Object_1 obj_1, Struct_1 stru_1)
{
obj_1.Age += 10;
obj_1.Name += "_Obj";
stru_1.Age += 10;
stru_1.Name += "_Stru";
}
得到结果:
可以发现,obj.Age与obj.Name的值发生了改变,但是stru.Age与stru.Name的值依然与之前一样。
在Change()方法内部obj_1与stru_1的值都被改变,但是为什么在执行完Change()之后却出现差异。那可以设想为我们所改变的stru_1实际上不是真正的源参数。查询资料后得知:
那是因为在Change()方法内部obj_1被视为与传进obj参数是一个对象,而stru_1只是将参数stru的值Copy了一份。所以我们在内部修改stru_1的值只是修改了这个“替身”的值,本体的值并未修改。
所以也可以得出结论:
值类型在方法体内作为参数被时,只是将值Copy一份使用,因而对其所做操作都无法对源参数产生影响。
引用类型在方法体内被作为参数时,内部对象与参数对象一致,对其所做的操作会影响到源参数。
PS:int型,long型,char型等数据类型都是struct,所以符合我们上面的结论。
- ref与out
继续深入:
有时,我们也希望能将值类型像引用类型一样使用,比如在方法内修改值类型也影响源参数。
C#内相应提供了两个关键词:ref,out (两者区别参考这里)
当使用这两个关键词之后,我们看看有什么变化,修改上面的代码如下:
private void Form1_Load(object sender, EventArgs e)
{
Object_1 obj = new Object_1();
obj.Age = 1;
obj.Name = "Mike";
Struct_1 stru = new Struct_1();
stru.Age = 2;
stru.Name = "Tim";
Change(obj,ref stru);//此处被修改
MessageBox.Show("obj.Age:" + obj.Age + "\nobj.Name:" + obj.Name + "\nstru.Age:" + stru.Age + "\nstru.Name:" + stru.Name);
}
public void Change(Object_1 obj_1, ref Struct_1 stru_1)//此处被修改
{
obj_1.Age += 10;
obj_1.Name += "_Obj";
stru_1.Age += 10;
stru_1.Name += "_Stru";
}
结果如下:
stru.Age与stru.Name的值发生了变化。这也达到了我们需求,值类型也可以像引用类型一样使用。
但是原因是什么?
原因在于方法体在执行时,编译器根据ref关键词将值类型与参数对象“等同”了起来,而不是将值Copy一份。
这所有的一切究其原因,是在方法体内有种机制,这种机制将参数加上"签名"(可以理解为与身份证号类似的唯一标识),当是引用类型时,签名与外侧参数相同,值类型时则使用不同的“签名”。除非使用了ref或out关键词,值类型的“签名”才与源参数一致。
- new对象
刚刚我们说的都是改变值,若是将new对象呢?继续修改上面Change()方法代码如下:
public void Change(Object_1 obj_1, ref Struct_1 stru_1)
{
obj_1 = new Object_1();//此处被修改
obj_1.Age += 10;
obj_1.Name += "_Obj";
stru_1.Age += 10;
stru_1.Name += "_Stru";
}
结果如下:
由于在Change()内obj_1对象被new了,所以新对象的“签名”与源参数的“签名”已经不同,所以方法内部值的改变不影响原来的对象。如果要使new对象影响源参数,依然可以使用ref。
public void Change(ref Object_1 obj_1, ref Struct_1 stru_1)
- string类型
有人说string类型是特殊的引用类型,因为它与值类型一样在方法内部改变后无法影响原参数,为了验证,继续修改代码如下:
private void Form1_Load(object sender, EventArgs e)
{
Object_1 obj = new Object_1();
obj.Age = 1;
obj.Name = "Mike";
Struct_1 stru = new Struct_1();
stru.Age = 2;
stru.Name = "Tim";
string str = "A";//此处被修改
Change(ref obj, ref stru, str);
MessageBox.Show("obj.Age:" + obj.Age + "\nobj.Name:" + obj.Name + "\nstru.Age:" + stru.Age + "\nstru.Name:" + stru.Name + "\nstr:" + str);//此处被修改
}
public void Change(ref Object_1 obj_1, ref Struct_1 stru_1, string str)//此处被修改
{
obj_1 = new Object_1();
obj_1.Age += 10;
obj_1.Name += "_Obj";
stru_1.Age += 10;
stru_1.Name += "_Stru";
str = "B";//此处被修改
}
结果如下:
表面上str的值没有改变,依然是“A”。但是不要只看表面,之所以依然显示为“A”,,是因为在这里string=运算符实际上等同于str=new String(new char[]{'B'});也就是new了一个新对象, 那str的“签名”也自然就与源参数不同,也就不会影响源参数(与 obj_1 = new Object_1()是同样的道理)。
作者:Mr.Jimmy
出处:https://www.cnblogs.com/JHelius
联系:yanyangzhihuo@foxmail.com
如有疑问欢迎讨论,转载请注明出处