今天稍有点空闲,本来想看看网页啥的,无奈老板坐镇,只好翻开《CLR VIA C#》
有些地方有点迷糊,准备敲代码试一下,打开学习用的项目,突然发现有个以前看《深入C#内存管理来分析值类型&引用类型,装箱&拆箱,堆栈几个概念组合之间的区别》遗留下来的问题
代码是这样子的
int i = 4;
object o = i;
object o2 = o;
Console.WriteLine(ReferenceEquals(o, o2)); //true
o = 8;
Console.WriteLine(ReferenceEquals(o, o2)); //false
Console.WriteLine("i={0}, o={1}, o2={2}", i, o, o2); //4, 8, 4, why?
无奈祭出ILDASM,这回现形了(略去Console.WriteLine之类的代码)
IL_0001: ldc.i4.4
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: box [mscorlib]System.Int32
IL_0009: stloc.1
IL_000a: ldloc.1
IL_000b: stloc.2
IL_000c: ldc.i4.8
IL_000d: box [mscorlib]System.Int32
IL_0012: stloc.1
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对象。所以下面的这段代码
String s1 = "Hello";
String s2 = "Hello";
Console.WriteLine(Object.ReferenceEquals(s1, s2));
结果是True