简介
大部分的开发语言都会有引用类型与值类型,基本上所有的开发人员都能理解两者之间的区别,并使用;这里讲述几个例子看一看引用类型有哪些“反常”的情况,借此深入了解一下C#中引用类型。
本文以下使用VS2013、.Net4.5、控制台应用程序。
1. Test Zero
下边这个例子只要不是初学者很容易看出来输出的结果。
(这里使用了Newtonsoft.Json,只是为了让结果直观一些)
static void Main(string[] args)
{
Dictionary<int, int> dic_1 = null;
Dictionary<int, int> dic_2 = new Dictionary<int, int>();
dic_1 = dic_2;
dic_2.Add(1, 1);
dic_2.Add(2, 2);
Console.WriteLine("dic_1: " + Newtonsoft.Json.JsonConvert.SerializeObject(dic_1));
Console.WriteLine("dic_2: " + Newtonsoft.Json.JsonConvert.SerializeObject(dic_2));
Console.ReadLine();
}
这就是引用类型最简单的表现——使用同一块内存地址;虽然的是修改dic_2,但是上边dic_1 = dic_2;将它们绑定起来,故而结果是相同的。
但是下边一个例子有那么一点点反常。
2. Test One
static void Main(string[] args)
{
test_one();
}
static void test_one()
{
Console.WriteLine("test_one");
Dictionary<int, int> dic_1 = new Dictionary<int, int>();
Dictionary<int, int> dic_2 = new Dictionary<int, int>();
dic_1.Add(1, 1);
dic_2.Add(1, 1);
test_one_part(dic_1, dic_2);
Console.WriteLine("dic_1:" + Newtonsoft.Json.JsonConvert.SerializeObject(dic_1));
Console.WriteLine("dic_2:" + Newtonsoft.Json.JsonConvert.SerializeObject(dic_2));
Console.ReadKey();
}
static void test_one_part(Dictionary<int, int> dic1, Dictionary<int, int> dic2)
{
dic1 = new Dictionary<int, int>();
dic1.Add(2, 2);
dic2.Add(2, 2);
}
猜一猜这里输出的结果会是什么?
最容易想到的便是dic1={2:2};dic2={1:1,2:2};
因为test_one_part()将dic1重新new了,并重新加入{2:2},dic2操作很常规。接下来看结果:
然而结果跟我们想要的差的很多,从结果看来dic1好像完全没有受到test_one_part()的任何影响,根据一般对于引用类型的了解,在test_one_part()内部的修改引用类型的传入参数,必然会像dic2一样将结果体现到test_one()中的dic_2。
这明显不符合我们认识的“引用类型”。
结论一:引用类型在其他方法中被重新new以后不会改变原有对象的值。
3. Test Two
再看一个稍微绕一点的:
static void Main(string[] args)
{
test_two();
}
static void test_two()
{
Console.WriteLine("test_two");
Dictionary<int, int> dic_1 = new Dictionary<int, int>();
Dictionary<int, int> dic_2 = new Dictionary<int, int>();
dic_1.Add(1, 1);
dic_2.Add(1, 1);
test_two_part(dic_1, dic_2);
Console.WriteLine("dic_1:" + Newtonsoft.Json.JsonConvert.SerializeObject(dic_1));
Console.WriteLine("dic_2:" + Newtonsoft.Json.JsonConvert.SerializeObject(dic_2));
Console.ReadKey();
}
static void test_two_part(Dictionary<int, int> dic1, Dictionary<int, int> dic2)
{
dic1 = dic2;
dic1.Add(2, 2);
dic2 = new Dictionary<int, int>();
dic2.Add(2, 2);
}
test_two()和刚才的test_one()一样,但是test_two_part()有了一些变动。
按照常规思路再次分析一下:
static void test_two_part(Dictionary<int, int> dic1, Dictionary<int, int> dic2)
{
//一开始dic1与dic2同值不同址,现在这里他们也同址
dic1 = dic2;
//追加了一个值,dic2同样的也拥有
dic1.Add(2, 2);
//dic1={1:1,2:2};dic2={1:1,2:2}
//dic2被重新new,由【结论一】可知,下边不会影响到输出,忽略
dic2 = new Dictionary<int, int>();
dic2.Add(2, 2);
//dic1={1:1,2:2};dic2={2:2}
}
代码看完了,初步的分析也有了,那么预测一般情况下的输出结果:
dic1={1:1,2:2};dic2={1:1,2:2};
执行结果如下图:
dic1又没有追加到值{2,2},然而dic2内却包含了{2,2},好吧再没有搞明白内部原理的情况下只能从结果倒推出一些猜测。
结论二:引用类型在其他方法中被重新赋值以后不会改变原有对象的值。
4. Test Three
下边这个例子可以帮我们理解上边的问题:
static void Main(string[] args)
{
Dictionary<int, int> dic_0 = new Dictionary<int, int>();
Dictionary<int, int> dic_1 = new Dictionary<int, int>();
Dictionary<int, int> dic_2 = new Dictionary<int, int>();
dic_1.Add(1, 1);
dic_2.Add(2, 2);
dic_0 = dic_1;
dic_1 = dic_2;
Console.WriteLine("dic_0: " + Newtonsoft.Json.JsonConvert.SerializeObject(dic_0));
Console.WriteLine("dic_1: " + Newtonsoft.Json.JsonConvert.SerializeObject(dic_1));
Console.WriteLine("dic_2: " + Newtonsoft.Json.JsonConvert.SerializeObject(dic_2));
Console.ReadLine();
}
结果如下:
虽然有dic_0 = dic_1;dic_1 = dic_2;
但是从结果看出来dic_0 != dic_2;并不是dic_0 = dic_1 = dic_2。
5. 总结
其实结论一与结论二是一样的,都是再dic1直接被赋值发生了改变,将另外的一个内存地址赋给了dic1;根本上只要理解的引用类型赋值的原理,这些就很容易理解;值类型的赋值的修改内存中存储的值,引用类型修改的是指向值的指针(内存地址)。
Tset_One与Test_Two分了两个方法test_two();test_two_part();第一个方法中的变量被传入第二个方法中时,应该理解为前一个变量的copy;test_two_part()中变量一旦重新赋值,内存地址即被修改,原有方法外的变量地址不会受到影响。两层方法有一定的迷惑,Test_Three中就可以明显看的出来端倪。
所以以上例子的原因就很明确了,我们的引用类型还是引用类型。
.
.
.
.
.
.
.
.
【本文章出自NM1024.com,转载请注明作者出处。】
>>转载请注明原文链接地址:C#引用类型的“反常情况”