将C#值元组与==进行比较时,为什么编译器会创建隐藏副本?
c#
c#-9.0
C# 7.3 中引入了值元组类型之间的相等性。它允许这样的代码:
var x = (1, 2);
var y = (1, 2);
if(x == y) ...
这工作正常并给出正确的结果。然而,编译器引入了元组对象的隐藏副本,并比较它们,给出等价的:
var V_3 = x;
var V_4 = y;
if(V_3.Item1 == V_4.Item1 && V_3.Item2 == V_4.Item2) ...
是V_3和V_4刚才的防守副本?如果是这样,他们在防御什么?
这是我能想到的最小的例子,但我已经尝试过使用用户定义的结构和方法返回/属性作为元组成员以及类似(并不总是相同)的输出。
我正在使用 .Net SDK 的 v5.0.202 和 C# v9,但从 .Net Core 3 开始就看到了这种行为。
回答
答案是 MSIL,您要反编译回 C# 的中间语言,是一种基于堆栈的语言,并且基于堆栈的语言很难引用不在堆栈最顶端的内容。因此,您可以添加一个局部变量并存储您无法轻松访问的结果。
这种看似低效的问题之所以没有得到优化,是因为它根本无关紧要。当 JITed 到本机代码时,编译器会发出直接比较和你期望的跳转:
var rng = new Random();
var v1=(rng.Next(), rng.Next());
var v2=(rng.Next(), rng.Next());
return v1 == v2;
生成(跳过初始化并恢复序言中的堆栈):
L004f: cmp edi, ebp
L0051: jne short L0064
L0053: cmp ebx, eax
L0055: sete al // == && ==?
L0058: movzx eax, al // return true && 2nd equality result
L0063: ret
L0064: xor eax, eax // return false
L006e: ret