为什么我可以将sbyte与除*ulong之外的所有其他数字类型进行比较?

Amy*_*Amy 25 .net c# clr

你可以在sbyte和byte,int,uint,short,ushort,long,double和float之间进行>,<,==等比较.但不是ulong.

我的大脑爆炸了.任何人都可以解释为什么sbyte可以与uint相比而不是 ulong?

public bool sbyte_ulong_compare(sbyte x, ulong y)
{
    return x < y;  // compiler error CS0019
}
Run Code Online (Sandbox Code Playgroud)

此外,使用unchecked不会使事情更好.大脑融化.

另一个编辑.这有效:

public bool sbyte_ulong_compare(sbyte x, ulong y)
{   
    //
    // returns x < y
    //
    if (x < 0)
        return true;

    if (y > 127)
        return true;

    return ((long)x < (long)y);
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 20

dthorpe和Jon的答案很接近但不太正确.

正确的推理如下.

规范说明:

对于x op y形式的操作,其中op是比较运算符,应用重载决策来选择特定的运算符实现.

好的,重载决策必须使用的运算符实现是什么?他们是:

bool operator <(int x, int y);
bool operator <(uint x, uint y);
bool operator <(long x, long y);
bool operator <(ulong x, ulong y);
bool operator <(float x, float y);
bool operator <(double x, double y);
bool operator <(decimal x, decimal y);
Run Code Online (Sandbox Code Playgroud)

加上所有枚举类型的enum less-than运算符,以及前述每个类型的提升到可空版本.

过载分辨率必须首先消除不适用的运算符,然后从剩余的适用运算符集中确定最佳运算符.

int,uint,long和enum运算符(及其提升的形式)都被删除了,因为ulong不会隐式转换为这些类型.

uint和ulong运算符(及其提升的形式)都被消除了,因为sbyte不会隐式转换为这些类型.

离开了

bool operator <(float x, float y);
bool operator <(double x, double y);
bool operator <(decimal x, decimal y);
Run Code Online (Sandbox Code Playgroud)

和他们提升的形式.我们现在必须确定这六个中最好的运营商.

"最好的"是什么意思?比较两个运算符时,具有更具体操作数类型的运算符更好.通过"更具体",我的意思是"老虎"比"动物"更具体,因为所有老虎都可以转换为动物但不是所有动物都可以转换为老虎.

显然,未提升的形式优于所有相应的提升形式.非可空类型比其对应的可空类型更具体,因为非可空类型总是可转换为其可空类型,但反之亦然.我们可以消除提升的形式.

剩下三个.这三个中哪一个最好?

浮动比双重更具体.每个浮动都可以转换为double,但不是每个double都可以转换为float.因此消除了双倍.剩下两个.

bool operator <(float x, float y);
bool operator <(decimal x, decimal y);
Run Code Online (Sandbox Code Playgroud)

哪个是最好的?没有从float到decimal的隐式转换.没有从decimal到float的隐式转换.因此,两者都不比另一个好.

因此,无法确定最佳操作员.重载解析失败.

我们已经决定报告一个通用的错误消息,它只是说没有这样的运算符可以做你想要的,而不是给出看似奇怪和混乱的错误消息"运算符重载决策失败,因为浮点既不好也不比十进制差".我认为这是一个合理的设计选择.

  • 我们有一个很好的工作`十进制'使这个失败.将`ulong`和`long`转换为`float`进行比较的想法是可怕的,给出了明显不合适的结果.我想如果我们这里没有`decimal`,我们真的必须有其他规则来防止这种情况发生. (13认同)

dth*_*rpe 7

比较不同整数类型的两个整数时,操作的类型是可以表示两个操作数组合的整个范围的最小整数类型.如果将带符号的字节与uint进行比较,则操作的类型很长,因为long有足够的范围来覆盖有符号字节的负部分和uint的正部分.

当您尝试比较sbyte和ulong时,没有整数类型可以跨越ulong和signed byte的负部分的范围.编译器仅考虑内置整数类型.不包括隐式提升到Decimal,因为Decimal不是整数类型,并且出于性能原因.

在第二个代码示例中,由于您已对操作数进行了预先限定,因此可以安全地将操作数类型转换为不跨越两个操作数范围的公共整数类型.另请注意,在第二个示例中,您可以对字节(而不是长)进行类型转换而不会丢失信息,因为您已经确定ulong值小于127且sbyte值为非负值.

C#编译器没有"看到"您已经对操作数进行了资格预审,并且逻辑上操作数中的值都在字节范围内,并且编译器本身不会生成执行此类预审的代码.

有些语言会发出类似于第二个示例的预审条件代码,以支持不具有公共超集类型的类型之间的比较.你为此获得了性能和内存(代码大小).C#可能不会在不想"奖励"不良编码实践的精神下发出这种预审资格代码.如果您要比较签名值和ulong,您需要了解并承担费用.

在语言理论中,有一个类型推理的分支,称为(我认为)类型代数,它跟踪变量的测试并动态地缩小变量类型的范围,因为在代码流中发现了新的约束.这种类型的推理形式允许您在第二个示例中比较操作数而不进行类型转换,因为它会看到您已将操作数预先限定为字节范围.C#不进行这种类型的推理.我认为Haskell或F#可能.

  • +1很棒的答案,非常完整.我认为你最后的意思是"Haskell"而不是"Hascal",除非有一个奇怪的Pascal变种我无法通过谷歌找到.:)此外,如果你对这个陈述有一个引用,那就更好了:*"由于性能原因,不包括隐式提升到十进制."* (2认同)