枚举定义中的加号运算符

本文关键字:运算符 定义 枚举 | 更新日期: 2023-09-27 18:24:40

我今天偶然发现了枚举定义中加号(+(运算符的使用,我很惊讶地看到随附的测试通过了。 有人知道这可能在哪里记录吗?

public enum ApprovalItemState
{
    Enqueued = 1,
    Approved = 2,
    Denied = 4,
    Acknowledged = 8,
    ApprovalAcknowledged = ApprovalItemState.Approved + ApprovalItemState.Acknowledged,
    DenialAcknowledged = ApprovalItemState.Denied + ApprovalItemState.Acknowledged
}

[TestClass]
public class ApprovalItemStateTests
{
    [TestMethod]
    public void AreFlagsDeniedAndAcknowledged()
    {
        Assert.AreEqual(ApprovalItemState.DenialAcknowledged, ApprovalItemState.Denied | ApprovalItemState.Acknowledged);
    }
    [TestMethod]
    public void IsDenialAcknowledged()
    {
        Assert.IsTrue(Enum.IsDefined(typeof(ApprovalItemState), ApprovalItemState.Denied | ApprovalItemState.Acknowledged));
        Assert.AreEqual(ApprovalItemState.Denied | ApprovalItemState.Acknowledged, (ApprovalItemState)Enum.Parse(typeof(ApprovalItemState), "DenialAcknowledged"));
    }

    [TestMethod]
    public void IsNotDeniedAndApproved()
    {
        Assert.IsFalse(Enum.IsDefined(typeof(ApprovalItemState), ApprovalItemState.Approved | ApprovalItemState.Denied));
    }
}

枚举定义中的加号运算符

里德的回答当然是正确的。我只是想添加一些有趣的琐事。 首先,当您在枚举时,枚举的所有成员都在范围内。这是 C# 中唯一可以通过枚举成员的非限定名称使用枚举成员的情况!

public enum ApprovalItemState 
{
    Enqueued = 1,
    Approved = 2,
    Denied = 4,
    Acknowledged = 8,
    ApprovalAcknowledged = Approved | Acknowledged,
    DenialAcknowledged =  Denied | Acknowledged 
} 

第二个琐事点是,C# 编译器实际上允许枚举算术涉及枚举内的其他枚举

enum E
{
    E1
}
enum F
{
    F1
}
enum G
{
    G1 = E.E1 + F.F1
}

通常这根本不合法;你不能将两个不同的枚举加在一起,也不能分配结果。编译器在枚举初始值设定项中放宽这些规则,以便您可以执行以下操作:

enum MyFlags
{
    MyReadOnly = FileFlags.ReadOnly,
    ...

C# 语言规范在 14.5 中指出:

以下运算符可用于枚举类型的值:==、!=、<、>、<=、>= (§7.10.5(、二进制 + (§7.8.4(、二进制 - (§7.8.5(、^、&、|(§7.11.2(、~ (§7.7.4(、++ 和 -- (§7.6.9 和 §7.7.5(。

基本上,由于枚举在内部存储为Int32(这是默认值,除非您指定不同的存储类型(,因此您可以使用这样的加法。

但是,使用|而不是+来定义掩码更为常见。 此外,如果要将其用作标志枚举,通常包含[Flags]

来自枚举上的 C# 参考:

。每个枚举类型都有一个基础类型,该基础类型可以是除 char 之外的任何整型类型。 枚举元素的默认基础类型是 int...

顺便说一下,使用 | 而不是+来组合枚举标志值更惯用(并且更不容易出错(。例如,此错误不会导致问题:

DenialAcknowledged =
    ApprovalItemState.Denied
    | ApprovalItemState.Acknowledged
    | ApprovalItemState.Denied

但是这个错误会导致一个问题:

DenialAcknowledged =
    ApprovalItemState.Denied
    + ApprovalItemState.Acknowledged
    + ApprovalItemState.Denied

Approved + Acknowledged只是一个常量,因此可以将其作为值分配给枚举元素。关于测试 - 它们有效是因为 int 值是"快乐的",所以(a + b) == (a | b)

但是,如果您将其更改为类似的东西:

public enum ApprovalItemState
{
    Enqueued = 1,
    Approved = 2,
    Denied = 7,
    Acknowledged = 18,
    ApprovalAcknowledged = Approved + Acknowledged,
    DenialAcknowledged = Denied + Acknowledged
}

并且测试不会通过。

并不奇怪 - 枚举由整数类型表示。 您也可以使用其他运算符,但如果要使用标志(此示例正在这样做(,最好使用 [Flags] 属性来定义它们,并且更好地布局位:

[Flags]
public enum ApprovalItemState
{
    Enqueued = 1 << 0,
    Approved = 1 << 1,
    Denied = 1 << 2,
    Acknowledged = 1 << 3,
    ApprovalAcknowledged = ApprovalItemState.Approved | ApprovalItemState.Acknowledged,
    DenialAcknowledged = ApprovalItemState.Denied | ApprovalItemState.Acknowledged
}

我将为您分解其中之一。

DenialAcknowledged = ApprovalItemState.Denied + ApprovalItemState.Acknowledged
DenialAcknowledged = 4 + 8
DenialAcknowledged = 12

对于此测试:

[TestMethod]
public void AreFlagsDeniedAndAcknowledged()
{
    Assert.AreEqual(ApprovalItemState.DenialAcknowledged, ApprovalItemState.Denied | ApprovalItemState.Acknowledged);
}

您正在检查:

ApprovalItemState.DenialAcknowledged == ApprovalItemState.Denied | ApprovalItemState.Acknowledged
12 == 4 | 8
12 == 0100 | 1000 //bitwise operation 
12 == 1100
12 == 12 //convert from binary to decimal

这就是测试通过的原因。通过查看代码并不完全简单。