int的一点事,读《深入C#内存管理来分析值类型&引用类型,装箱&拆箱,堆栈几个概念组合之间的区别》

今天稍有点空闲,本来想看看网页啥的,无奈老板坐镇,只好翻开《CLR VIA C#》

有些地方有点迷糊,准备敲代码试一下,打开学习用的项目,突然发现有个以前看《深入C#内存管理来分析值类型&引用类型,装箱&拆箱,堆栈几个概念组合之间的区别》遗留下来的问题

代码是这样子的

1             int i = 4;
2             object o = i;
3             object o2 = o;
4             Console.WriteLine(ReferenceEquals(o, o2)); //true
5             o = 8;
6             Console.WriteLine(ReferenceEquals(o, o2)); //false
7 
8             Console.WriteLine("i={0}, o={1}, o2={2}", i, o, o2); //4, 8, 4, why?

结果原帖子里也有,但是为什么,不太明白,只是知道和装箱有点关系(int转成object当然要装箱)。查引用,上面代码第4行,说明两个指向的是同一个对象,但o=8之后,两个就不是同一个对象了。这是为啥?

无奈祭出ILDASM,这回现形了(略去Console.WriteLine之类的代码)

 1   IL_0001:  ldc.i4.4
 2   IL_0002:  stloc.0
 3   IL_0003:  ldloc.0
 4   IL_0004:  box        [mscorlib]System.Int32
 5   IL_0009:  stloc.1
 6   IL_000a:  ldloc.1
 7   IL_000b:  stloc.2
 8   IL_000c:  ldc.i4.8
 9   IL_000d:  box        [mscorlib]System.Int32
10   IL_0012:  stloc.1

1. 创建一个常数,值为4,入栈

2. 出栈,将值放入第一个内存变量(int i = 4)

3. 将第一个内存变量入栈

4. 装箱(object o = i)

5. 出栈,将o的地址放入第二个内存变量

6. 将第二个内存变量入栈

7. 出栈,将地址放入第三个内存变量(o的地址)(object o2 = o)

8. 创建一个常数,值为8,入栈

9. 装箱

10. 出栈,将装箱后的地址放入第二个内存变量(o = 8)

这样看来就比较清楚了,o2和o的地址明显不一样,因为o=8中有一个对8的装箱操作,之后仅修改了第二个内存变量中保存的地址,而第三个内存变量中保存的仍然是上次装箱4时的地址,所以o=8, o2=4

顺便说一下string吧

string是不可变的,也就是说,一个string对象创建以后,如果修改它的内容,就会创建一个新的string对象,换句话说,地址变了。《CLR VIA C#》中的解释是string pooling,所有内容相同的字符串对象都会指向同一个metadata中的同一个string对象。所以下面的这段代码

1             String s1 = "Hello";
2             String s2 = "Hello";
3             Console.WriteLine(Object.ReferenceEquals(s1, s2));

结果是True

原文地址:https://www.cnblogs.com/kofkyo/p/2526535.html