为什么我需要一个中间转换来将结构体转换为十进制,但不需要将结构体转换为整数?

8

我有一个这样的结构体,其中包含一个显式转换为float的方法:

struct TwFix32
{
    public static explicit operator float(TwFix32 x) { ... }
}

我可以通过一个显式的单一强制类型转换将TwFix32转换为int: (int)fix32

但是要将其转换为十进制数,我需要使用两个转换: (decimal)(float)fix32

从float到int或十进制数都没有隐式转换。为什么当我要去int时编译器允许我省略中间的float转换,但是当我要去十进制数时就不行了呢?

1个回答

8

我经常对“为什么”问题无法给出满意的答案。

C#编译器表现出这种行为的原因是它(在这种情况下至少)正确实现了C#规范

规范的第6.4.5节描述了如何分析用户定义的转换。仔细阅读该部分将解释为什么显式转换为int是合法的,但转换为decimal则不合法。

具体而言,相关段落如下:

查找适用的用户定义和提升的转换运算符U。此集合由D中声明的类或结构中定义的从包含或被包含于S的类型转换为包含或被包含于T的类型的用户定义和提升的隐式或显式转换运算符组成。如果U为空,则转换未定义,并发生编译时错误。

在您的情况下,S是TwFix,T是int或decimal。 TwFix上唯一的用户定义显式转换返回float。 int被包含在float中,但decimal既不被包含也不包含float。因此,在一个情况下,集合U有一个成员,在另一个情况下为空。因此,一个情况产生错误,正如规范所说的那样,而另一个情况则没有。

我觉得这个答案不太令人满意。如果可以,您能否重新表述问题,使其不包含“为什么”这个词?对于“什么”或“如何”问题,我会回答得更好。

(*)编译器在计算一个类型是否包含另一个类型以确定分析特定用户定义转换的语义时的代码中存在已知的错误。 在许多情况下,我们有意不修复这些错误,因为这样做将在现实世界中引入破坏性变化,而收益微乎其微。 我非常希望重新审查规范的这一部分,并重写它以消除“包含类型”的概念;它是规范中的一个奇点。 正如您所发现的那样,它产生了这种奇怪的现象,即float可以显式转换为decimal,而decimal可以显式转换为float,但由于两者都不包含对方,用户定义的显式转换代码不喜欢它。 但是这非常低优先级。


1
我的感觉是,根本问题是为什么(是的,我知道)从int到float存在隐式转换,应该影响相反方向的转换(TwFix32到float到int)。在这种情况下,这对我来说感觉上应该是一个无关紧要的转换 - 但显然不是,因为它会影响到包含它的转换。我怀疑真正的“为什么”答案可能是,“因为它使许多其他情况按预期工作,以此为代价产生了这种奇怪的现象。” - Jon Skeet
1
@Jon:没错;如果你从分析引用类型上的用户定义转换的角度来考虑这些规则,它会更有意义;非接口引用类型通常不会出现像float/decimal这样的情况。 - Eric Lippert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接