我在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#中的错误,如果不是,为什么?
答案 0 :(得分:13)
这是C#中的错误吗?
没有
为什么不呢?
因为此处演示的行为与规范一致。仔细阅读规范的6.4.4和6.4.5节。 (请注意,C#编译器有许多方法与规范的这一部分不一致;实现在不明确的极端情况下存在许多错误,特别是涉及提升的运算符。)
我希望将其转换为uint并丢失数据是非法的。
不幸的是,你不能总是得到你想要的东西。
当C#允许我毫无怨言地投射到uint并且即使高位丢失也没有抛出异常时,你可以想象我的惊讶。
这有点令人惊讶,我同意。
规则是:允许每个用户定义的转换都在输入和输出上插入标准转换。此外,如果它是显式转换 - 也就是说,您正在进行转换 - 那么插入的标准转换可能是隐式或显式标准转换。 (事实上,强制转换可能会导致显式标准转换插入到用户定义的隐式转换中!)
在您的情况下,存在从ULongWrapper到ulong的显式用户定义转换,以及从ulong到uint的标准显式转换。因此,将ULongWrapper转换为uint是合法的。
您可能会考虑不首先实现显式转换,因为显然您不喜欢显式转换的语义。只需编写一个返回底层ulong的方法或属性,并完成它。
答案 1 :(得分:2)
uint
和ulong
之间存在显式转换。您允许转换为ulong
,并且您明确地转换为uint
。你能指望什么?你告诉编译器“别担心,我知道我在做什么”。
然后以某种方式隐式地将该结果转换为uint
没有什么隐含的。你正在表演。这对我来说似乎是过于防守的编程。
答案 2 :(得分:1)
在强制转换期间发生溢出,但溢出检查是在C#中“选择加入”。 将演员包裹在:
checked
{
..
}
所以它真的是“按设计”而不是错误。在我认为编译时,返回ulong总是可以转换的。