警惕值类型的陷阱(补充)
在警惕值类型的陷阱中,乌卡卡同学指出第一个示例之所以出现问题,是因为委托的实现方式不对。
我能想到的“委托的实现方式”除了这种正常的赋值,剩下的就是用Lambda表达式或匿名方法了了,于是我把代码改为如下的形式:
struct MyStruct { public int value; public void SetValue(int value) { this.value = value; } } class Program { static void Main() { var ms = new MyStruct(); Action<int> action = (i) => ms.SetValue(i); action(1); Console.WriteLine(ms.value); Console.ReadLine(); } }
运行,奇迹发生了,结果是1!
为什么改成Lambda表达式之后结果完全不一样了呢?我本能的反应是没有发生装箱操作,但一时还搞不明白到底是怎么回事,于是迫不及待地打开IL查看:
.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       65 (0x41)
  .maxstack  3
  .locals init ([0] class [mscorlib]System.Action`1
MyTest.MyStruct MyTest.Program/'<>c__DisplayClass1'是编译器为Lambda表达式自动生成的类。因为Lambda表达式也好,匿名方法也好,都需要存在于类之中。上面的代码与下面是一样的:
Action<int> action = delegate(int i) { ms.SetValue(i); };
看到这里,我想您应该已经明白了。这里用于委托_target字段的是匿名方法所在的类<>c__DisplayClass1,而不是装箱之后的MyStruct,因此不存在装箱操作。我们只是在匿名方法内部调用了SetValue方法,并把该匿名方法作为委托的方法,而不是SetValue方法。也就是说委托的Invoke调用的是匿名方法,而不是SetValue方法。
我想这就应该是乌卡卡同学所指出的,换一种委托形式以避免装箱的方法吧。
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号