`flags &= (ushort)~Mask;` 在 `Mask` 是 `const` 时为什么有问题?

huangapple go评论59阅读模式
英文:

Why is `flags &= (ushort)~Mask;` problematic when `Mask` is `const`?

问题

以下是翻译的内容:

以下代码引发了编译错误 CS0221:*常量值 '-2' 无法转换为 'ushort'(使用 'unchecked' 语法来覆盖)*

private ushort _fooFlags = 0;
public void SetFoo1Flag(bool foo)
{
    const ushort Foo1Mask = 0x0001;
    if (foo)
    {
        _fooFlags |= Foo1Mask;
    }
    else
    {
        _fooFlags &= (ushort)~Foo1Mask; // CS0221 错误
    }
}

如提示所示,使用 unchecked 修复了此问题。

private ushort _fooFlags = 0;
public void SetFoo1Flag(bool foo)
{
    const ushort Foo1Mask = 0x0001;
    if (foo)
    {
        _fooFlags |= Foo1Mask;
    }
    else
    {
        unchecked
        {
            _fooFlags &= (ushort)~Foo1Mask;
        }
    }
}

我主要是一个C语言编程者,因此我尝试了各种变化来了解这些(基本的)整数操作的行为。

移除 const 是可以的...

public void SetFoo2Flag(bool foo)
{
    ushort Foo2Mask = 0x0001;
    if (foo)
    {
        _fooFlags |= Foo2Mask;
    }
    else
    {
        _fooFlags &= (ushort)~Foo2Mask;
    }
}

...同样,用 const 替换为 readonly 也可以。

private readonly ushort Foo3Mask= 0x0004;
public void SetFoo3Flag(bool foo)
{
    if (foo)
    {
        _fooFlags |= Foo3Mask;
    }
    else
    {
        _fooFlags &= (ushort)~Foo3Mask;
    }
}

我使用了显式转换,因此我期望 (ushort)~Foo1Mask 成为 ushort,而不管 ~Foo1Mask 发生了什么。

const 有何不同之处,以至于需要特殊处理,而另外两个(在我看来)相似的变化按我所期望的方式工作?

附注:我知道在这个示例中,我本可以使用枚举和 [Flags]

英文:

The following code throws a compiler error CS0221: Constant value '-2' cannot be converted to a 'ushort' (use 'unchecked' syntax to override)

private ushort _fooFlags = 0;
public void SetFoo1Flag(bool foo)
{
    const ushort Foo1Mask = 0x0001;
    if (foo)
    {
        _fooFlags |= Foo1Mask;
    }
    else
    {
        _fooFlags &= (ushort)~Foo1Mask; // CS0221 error
    }
}

As the hint suggests, using unchecked fixes this issue.

private ushort _fooFlags = 0;
public void SetFoo1Flag(bool foo)
{
    const ushort Foo1Mask = 0x0001;
    if (foo)
    {
        _fooFlags |= Foo1Mask;
    }
    else
    {
        unchecked
        {
            _fooFlags &= (ushort)~Foo1Mask;
        }
    }
}

I'm primarily a C coder, so I tried variations to see how these (basic) integer operations behave.

Removing const works fine..

public void SetFoo2Flag(bool foo)
{
    ushort Foo2Mask = 0x0001;
    if (foo)
    {
        _fooFlags |= Foo2Mask;
    }
    else
    {
        _fooFlags &= (ushort)~Foo2Mask;
    }
}

..as well as replacing const with readonly.

private readonly ushort Foo3Mask= 0x0004;
public void SetFoo3Flag(bool foo)
{
    if (foo)
    {
        _fooFlags |= Foo3Mask;
    }
    else
    {
        _fooFlags &= (ushort)~Foo3Mask;
    }
}

I'm using an explicit cast, so I'd expect (ushort)~Foo1Mask to become a ushort, regardless of what happens with ~Foo1Mask.

What makes const so different, that it requires special treatment, while the other two (in my view) similar variations work as I have expected?

PS: I know in this example I could've used an enum and [Flags].

答案1

得分: 1

The ~ operator is supported for the following types: "int, uint, long, and ulong" as per the documentation. There's an implicit conversion from ushort to all of those, but int is the "smallest" of those types, so it "wins" the fight and becomes the type of ~anyUshortExpression.

An operation can also be performed at compile time when the expression involves entirely literals, constants, or other expressions that can be resolved at compile time. This means that ~Foo1Mask will be treated identically to the compiler as if you'd have written -2. Which is why you see that in the compiler error you get.

As far as why you only see it when you use a constant, if you don't, then at compile time it has no idea that the int that you're trying to cast to a ushort won't fit inside of a ushort. It can't know that until runtime when it actually tries to execute the cast (at which point it will throw in a checked context or wrap around in an unchecked context). So it just takes your word for it at compile time, even in a checked context, since it has no other choice.

英文:

The ~ operator is supported for the following types: "int, uint, long, and ulong" as per the documentation. There's an implicit conversion from ushort to all of those, but int is the "smallest" of those types, so it "wins" the fight and becomes the type of ~anyUshortExpression.

An operation can also be performed at compile time when the expression involves entirely literals, constants, or other expressions that can be resolved at compile time. This means that ~Foo1Mask will be treated identically to the compiler as if you'd have written -2. Which is why you see that in the compiler error you get.

As far as why you only see it when you use a constant, if you don't, then at compile time it has no idea that the int that you're trying to cast to a ushort won't fit inside of a ushort. It can't know that until runtime when it actually tries to execute the cast (at which point it will throw in a checked context or wrap around in an unchecked context). So it just takes your word for it at compile time, even in a checked context, since it has no other choice.

huangapple
  • 本文由 发表于 2023年2月16日 03:28:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/75464597.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定