加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 整体类型促销不一致

发布时间:2020-12-15 08:09:03 所属栏目:百科 来源:网络整理
导读:using System;public class Tester{ public static void Main() { const uint x=1u; const int y=-1; Console.WriteLine((x+y).GetType()); // Let's refactor and inline y... oops! Console.WriteLine((x-1).GetType()); }} 想象一下上面的代码在以下情况
using System;

public class Tester
{
    public static void Main()
    {
        const uint x=1u;
        const int y=-1;
        Console.WriteLine((x+y).GetType());
        // Let's refactor and inline y... oops!
        Console.WriteLine((x-1).GetType());
    }
}

想象一下上面的代码在以下情况中使用:

public long Foo(uint x)
{
    const int y = -1;
    var ptr = anIntPtr.ToInt64() + (x + y) * 4096;
    return ptr;
}

内联y看起来非常安全,但事实并非如此.语言本身的这种不一致是违反直觉的,而且非常危险.大多数程序员只是内联y,但实际上你最终会出现整数溢出错误.事实上,如果你编写如上所述的代码,你很容易让下一个人在同一条代码内联工作,甚至没有考虑过两次.

我认为这是C#的一个非常适得其反的语言设计问题.

第一个问题,在C#规范中定义了这种行为,为什么这样设计?

第二个问题,1.GetType()/( – 1).GetType()给出了System.Int32.那么为什么它与const int y = -1的行为不同?

第三个问题,如果隐式转换为uint,那么我们如何明确地告诉编译器它是一个有符号整数(1i不是有效的语法!)?

最后一个问题,这不可能是语言设计团队所期望的行为(Eric Lippert要编钟吗?),可以吗?

解决方法

此行为由C#标准的第6.1.9节描述,隐式常量表达式转换:

? A constant-expression (§7.19) of type int can be converted to type sbyte,byte,short,ushort,uint,or ulong,provided the value of the constant-expression is within the range of the destination type.

所以你有常数x = 1u;和常量表达式(x – 1).

根据规范,x-1的结果通常是int,但由于常量表达式(即0)的值在uint的范围内,因此将其视为uint.

请注意,这里编译器将1视为无符号.

如果将表达式更改为(x -1),则将-1视为signed,并将结果更改为int. (在这种情况下,– in -1是一个“一元运算符”,它将-1的结果类型转换为int,因此编译器不能再像普通1那样将它转换为uint.

规范的这一部分暗示如果我们将常量表达式更改为x – 2,那么结果将不再是uint,而是转换为int.但是,如果进行了更改,则会收到编译错误,指出结果将溢出uint.

这是因为C#规范的另一部分,在7.19节常量表达式中指出:

The compile-time evaluation of constant expressions uses the same rules as run-time evaluation of non-constant expressions,except that where run-time evaluation would have thrown an exception,compile-time evaluation causes a compile-time error to occur.

在这种情况下,如果进行检查计算会出现溢出,因此编译器会发生故障.

关于这个:

const uint x = 1u;
const int y = -1;
Console.WriteLine((x + y).GetType()); // Long

这与此相同:

Console.WriteLine((1u + -1).GetType()); // Long

这是因为-1的类型为int,而1u的类型为uint.

第7.3.6.2节二进制数字促销描述了这个:

?否则,如果任一操作数的类型为uint而另一个操作数的类型为sbyte,short或int,则两个操作数都将转换为long类型.

(我省略了与此特定表达式无关的部分.)

附录:我只是想指出常数和非常数值之间的一元减号(又称“否定”)运算符的细微差别.

根据标准:

If the operand of the negation operator is of type uint,it is converted to type long,and the type of the result is long.

变量也是如此:

var p = -1;
Console.WriteLine(p.GetType()); // int

var q = -1u;
Console.WriteLine(q.GetType()); // long

var r = 1u;
Console.WriteLine(r.GetType()); // uint

虽然对于编译时常量,如果涉及uint的表达式使用它,则将值1转换为uint,为了将整个表达式保持为uint,-1实际上被视为int.

我同意OP – 这是非常微妙的东西,导致各种惊喜.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读