Tho*_*oub 125 .net c# language-lawyer
出于某种原因,我偷偷进入了该类的.NET Framework源代码,Double并发现声明==是:
public static bool operator ==(Double left, Double right) {
return left == right;
}
Run Code Online (Sandbox Code Playgroud)
每个操作员都适用相同的逻辑.
D S*_*ley 62
实际上,编译器会将==运算符转换为ceqIL代码,并且不会调用您提到的运算符.
源代码中运算符的原因很可能是因为它可以从C#以外的语言中调用,而不是CEQ直接将其转换为调用(或通过反射).代码中的操作将被编译为一个CEQ,所以没有无限递归.
实际上,如果通过反射调用运算符,则可以看到运算符被调用(而不是CEQ指令),并且显然不是无限递归(因为程序按预期终止):
double d1 = 1.1;
double d2 = 2.2;
MethodInfo mi = typeof(Double).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public );
bool b = (bool)(mi.Invoke(null, new object[] {d1,d2}));
Run Code Online (Sandbox Code Playgroud)
产生的IL(由LinqPad 4编译):
IL_0000: nop
IL_0001: ldc.r8 9A 99 99 99 99 99 F1 3F
IL_000A: stloc.0 // d1
IL_000B: ldc.r8 9A 99 99 99 99 99 01 40
IL_0014: stloc.1 // d2
IL_0015: ldtoken System.Double
IL_001A: call System.Type.GetTypeFromHandle
IL_001F: ldstr "op_Equality"
IL_0024: ldc.i4.s 18
IL_0026: call System.Type.GetMethod
IL_002B: stloc.2 // mi
IL_002C: ldloc.2 // mi
IL_002D: ldnull
IL_002E: ldc.i4.2
IL_002F: newarr System.Object
IL_0034: stloc.s 04 // CS$0$0000
IL_0036: ldloc.s 04 // CS$0$0000
IL_0038: ldc.i4.0
IL_0039: ldloc.0 // d1
IL_003A: box System.Double
IL_003F: stelem.ref
IL_0040: ldloc.s 04 // CS$0$0000
IL_0042: ldc.i4.1
IL_0043: ldloc.1 // d2
IL_0044: box System.Double
IL_0049: stelem.ref
IL_004A: ldloc.s 04 // CS$0$0000
IL_004C: callvirt System.Reflection.MethodBase.Invoke
IL_0051: unbox.any System.Boolean
IL_0056: stloc.3 // b
IL_0057: ret
Run Code Online (Sandbox Code Playgroud)
有趣的是-同一运营商不存在(无论是在参考源或通过反射)的整数类型,只有Single,Double,Decimal,String,和DateTime,它们的存在是从其他语言中,它驳斥我的理论.显然,如果没有这些算子,你可以将其他语言中的两个整数等同起来,所以我们回到"它们为什么存在double"的问题?
Lua*_*aan 37
这里的主要困惑是你假设所有的.NET库(在这种情况下,扩展的数字库,它不是 BCL的一部分)是用标准的C#编写的.情况并非总是如此,不同的语言有不同的规则.
在标准C#中,由于操作符重载解析的工作方式,您看到的代码段会导致堆栈溢出.但是,代码实际上并不是标准的C# - 它基本上使用了C#编译器的未记录的功能.它不会调用运算符,而是发出以下代码:
ldarg.0
ldarg.1
ceq
ret
Run Code Online (Sandbox Code Playgroud)
就是这样:)没有100%等效的C#代码 - 这在C#中是不可能的,只有你自己的类型.
即使这样,编译C#代码时也不会使用实际的运算符 - 编译器会进行一系列优化,就像在这种情况下一样,它只用op_Equality简单的方法替换调用ceq.同样,你不能在你自己的DoubleEx结构中复制它 - 它是编译器的魔力.
这当然不是.NET中的独特情况 - 有大量代码无效,标准C#.原因通常是(a)编译器黑客和(b)不同的语言,奇怪的(c)运行时黑客攻击(我正在看着你,Nullable!).
由于Roslyn C#编译器是oepn源代码,我实际上可以指出你决定重载决策的地方:
当您查看快捷方式时,您将看到double和double之间的相等性导致内部double运算符,而不是在==类型上定义的实际运算符中..NET类型系统必须假装它Double是一个类似于任何其他类型的类型,但C#不是 - double在C#中是原始类型.
Gyö*_*zeg 12
原始类型的来源可能令人困惑.你见过Double结构的第一行了吗?
通常你不能像这样定义一个递归结构:
public struct Double : IComparable, IFormattable, IConvertible
, IComparable<Double>, IEquatable<Double>
{
internal double m_value; // Self-recursion with endless loop?
// ...
}
Run Code Online (Sandbox Code Playgroud)
原始类型在CIL中也有其原生支持.通常它们不会被视为面向对象的类型.如果float64在CIL中使用double,则double只是一个64位值.但是,如果它作为通常的.NET类型处理,它包含一个实际值,它包含任何其他类型的方法.
所以你在这里看到的是运营商的情况.通常,如果直接使用double类型,则永远不会调用它.BTW,它的来源在CIL中看起来像这样:
.method public hidebysig specialname static bool op_Equality(float64 left, float64 right) cil managed
{
.custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor()
.custom instance void __DynamicallyInvokableAttribute::.ctor()
.maxstack 8
L_0000: ldarg.0
L_0001: ldarg.1
L_0002: ceq
L_0004: ret
}
Run Code Online (Sandbox Code Playgroud)
如您所见,没有无限循环(使用ceq仪器而不是调用System.Double::op_Equality).因此,当double被视为对象时,将调用operator方法,最终将其作为float64CIL级别的基本类型处理.
我用JustDecompile 看了一下CIL.内部==被转换为CIL ceq操作码.换句话说,它是原始的CLR平等.
我很想知道C#编译器在比较两个double值时是否会引用ceq或==运算符.在我提出的简单例子中(下面),它使用了ceq.
这个程序:
void Main()
{
double x = 1;
double y = 2;
if (x == y)
Console.WriteLine("Something bad happened!");
else
Console.WriteLine("All is right with the world");
}
Run Code Online (Sandbox Code Playgroud)
生成以下CIL(注意带标签的语句IL_0017):
IL_0000: nop
IL_0001: ldc.r8 00 00 00 00 00 00 F0 3F
IL_000A: stloc.0 // x
IL_000B: ldc.r8 00 00 00 00 00 00 00 40
IL_0014: stloc.1 // y
IL_0015: ldloc.0 // x
IL_0016: ldloc.1 // y
IL_0017: ceq
IL_0019: stloc.2
IL_001A: ldloc.2
IL_001B: brfalse.s IL_002A
IL_001D: ldstr "Something bad happened!"
IL_0022: call System.Console.WriteLine
IL_0027: nop
IL_0028: br.s IL_0035
IL_002A: ldstr "All is right with the world"
IL_002F: call System.Console.WriteLine
IL_0034: nop
IL_0035: ret
Run Code Online (Sandbox Code Playgroud)