当没有这样的运算符时,显式强制转换为uint

本文关键字:转换 uint 运算符 | 更新日期: 2023-09-27 18:26:42

我已经用一个私有数据成员编写了一个简单的ulong包装器。我希望能够将包装转换为ulong来检索数据。我希望强制转换为uint并丢失数据是非法的,所以我没有编写对uint的显式强制转换。你可以想象,当C#允许我毫无怨言地转换到uint,并且即使高位丢失也没有抛出异常时,我会感到惊讶。

这是我的测试代码:

class Program {
    static void Main(string[] args) {
        ULongWrapper a = new ULongWrapper(0xfffffffffUL);
        ulong b = (ulong)a;
        uint c = (uint)a;
        Console.WriteLine("{0:x}", b);
        Console.WriteLine("{0:x}", c);
    }
}
class ULongWrapper {
    private ulong data;
    public ULongWrapper(ulong data) {
        this.data = data;
    }
    public static explicit operator ulong(ULongWrapper x) {
        return x.data;
    }
}

打印:

fffffffff
ffffffff

这似乎是不需要的行为,因为我希望uint的强制转换在编译时失败!编译器使用ulong显式强制转换运算符,然后以某种方式将结果隐式强制转换为uint,而不进行边界检查。这是C#中的一个错误吗?如果不是,为什么?

当没有这样的运算符时,显式强制转换为uint

这是C#中的一个错误吗?

没有。

为什么不呢?

因为这里展示的行为与规范一致。仔细阅读本规范第6.4.4节和第6.4.5节。(请注意,C#编译器在许多方面与规范的这一部分不一致;该实现在模糊的角落情况下有许多错误,特别是涉及提升运算符。)

我希望强制转换为uint并丢失数据是非法的。

不幸的是,你不可能总是得到你想要的。

你可以想象,当C#允许我毫无怨言地抛出uint,并且即使高位丢失也没有抛出异常时,我会感到惊讶。

我同意,这有点令人惊讶。

规则是:每个用户定义的转换都允许在输入输出上插入标准转换。此外,如果是显式转换,也就是说,您正在执行强制转换,则插入的标准转换可能是隐式或显式标准转换。(事实上,强制转换可能会导致显式标准转换插入到用户定义的隐式转换中!)

在您的案例中,有一个从ULongWrapper到ulong的显式用户定义转换,以及从ulong到uint的标准显式转换。因此,将ULongWrapper强制转换为uint是合法的。

您可能首先考虑不实现显式转换,因为显然您不喜欢显式转换的语义。只需编写一个返回底层ulong的方法或属性,然后使用它即可。

uintulong之间存在显式转换。您允许转换为ulong,并显式地强制转换为uint。你期待什么?你是在告诉编译器"别担心,我知道我在做什么"。

然后以某种方式隐式地将结果转换为uint

这并没有什么隐含的。你在表演演员阵容。对我来说,这似乎是过于防御性的编程。

在强制转换过程中会发生溢出,但溢出检查在C#中是"选择加入"的。包裹铸件:

checked 
{
  ..
}

所以它实际上是"设计出来的",而不是一个bug。我认为,返回一个ulong在编译时总是可以抛出的。