为什么在 C# 中隐式调用显式十进制 => long 转换运算符,从而丢失精度?

kaa*_*lus 19 c# decimal implicit long-integer

以下 C# 程序以静默方式隐式调用显式小数到长整型转换运算符,从而丢失精度。

我不明白为什么会发生这种情况。据我了解,在 C# 中,显式运算符不应由语言隐式调用。特别是在这种情况下,静默显式转换正在失去精度(1.1M => 1L)。

这种奇怪的行为实际上导致了我的程序中的错误。

这是简化的代码:

// Custom number class
struct Num
{
    long Raw;
    public static implicit operator Num(long v) => new Num { Raw = v };
}

class Program
{
    static void Main()
    {
        decimal d = 1.1m;
        // The following line implicitly converts d to long (silently losing precision), 
        // then calls Num.op_Implicit(long)
        Num num = (Num)d;  // <=== should not compile???
    }
}
Run Code Online (Sandbox Code Playgroud)

这是生成的 IL。您可以看到它System.Decimal::op_Explicit被调用了,即使它从未被要求过。

IL_0000: ldc.i4.s 11
IL_0002: ldc.i4.0
IL_0003: ldc.i4.0
IL_0004: ldc.i4.0
IL_0005: ldc.i4.1
IL_0006: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, uint8)
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: call int64 [mscorlib]System.Decimal::op_Explicit(valuetype [mscorlib]System.Decimal)  // <=== ???
IL_0012: call valuetype Num Num::op_Implicit(int64)
IL_0017: stloc.1
IL_0018: ret
Run Code Online (Sandbox Code Playgroud)

Iri*_*ium 13

C# 规范中给出了用户定义的显式转换的规则。10.5.5 用户定义的显式转换。规则相当复杂,但特别允许执行额外的显式转换 \xe2\x80\x9cimplicitly\xe2\x80\x9d:

\n
\n

\xe2\x80\xa6\n如果E尚不具有类型,则执行从到 的S\xe2\x82\x93标准显式转换。\n\xe2\x80\xa6ES\xe2\x82\x93

\n
\n

(这里E是源类型 - decimal,S\xe2\x82\x93也是转换运算符从 - 转换而来的类型long)。

\n

请注意,您正在执行显式转换Num(即使它使用您定义的隐式运算符),因此遵循显式转换的规则,这允许额外的 \xe2\x80\x9ciimplicit\xe2\x80\x9d 显式转换decimal到long。

\n