为什么在一个枚举内声明位字段的组合与在枚举外声明会产生不同的结果?

问题描述 投票:1回答:1

在这里,我有一个由位字段表示的主题列表,底部有一个包含可选主题的 "可选 "字段。

[Flags]
enum Subjects 
{
    Art         = 0b_0000_0001,
    Agriculture = 0b_0000_0010,
    English     = 0b_0000_0100,
    Geography   = 0b_0000_1000,
    Maths       = 0b_0001_0000,
    Science     = 0b_0010_0000,
    Optional    = Art | Agriculture,
}

当我将可选科目打印到控制台时,我得到了一个意外的结果。

Console.WriteLine(Subjects.Optional); // returns "Optional", I expected "Art, Agriculture"

现在,如果我在枚举外声明同样的可选字段并记录下来,

// NOTE: I had to comment out the "Optional" field, otherwise it would return Optional once again

var optional = Subjects.Art | Subjects.Agriculture;
Console.WriteLine(optional); // returns "Art, Agriculture" not "Optional"

它就能正常工作

所以我的问题是,为什么我将组合位字段放在enum中与放在enum之外时,收到的输出不同?

c# enums bit-fields bitwise-or
1个回答
2
投票

你并没有区分枚举值和变量,但这些是非常不同的。


滥用枚举

旁白,我认为你在滥用枚举的目的,试图将一些关于这些枚举值的额外元数据(即它们是否是可选的)偷偷放到组成的 Optional 字段。

我怀疑对你来说,最好的解决办法是完全放弃使用枚举,因为枚举值不应该有更多的元田围绕着它们。

我仍然回答了这个问题,因为我对enum滥用的怀疑完全是基于一个名字和我对它对你的意义的解释。是你想在enum中偷偷加入一些元数据,还是我误解了你的意图,由你来决定。


枚举值

[Flags]
enum Subjects 
{
    Art         = 0b_0000_0001,
    Agriculture = 0b_0000_0010,
    Optional    = Art | Agriculture,
}

当你把组成的值包含在枚举中时,你把它定义为一个有效的枚举值。你实际上是在告诉编译器 Subjects.Optional 是一个有效的(因此是有意义的)枚举值,这意味着这个可以而且应该被使用。

这就导致编译器使用 Subjects.Optional 值(和它的字符串表示法,即 "Optional"),因为你告诉编译器它对你是有意义的。

变量

[Flags]
enum Subjects 
{
    Art         = 0b_0000_0001,
    Agriculture = 0b_0000_0010
}

var optional = Subjects.Art | Subjects.Agriculture;

重要的是要认识到 optional 是一个 可变的 而不是一个枚举值。这里只有两个枚举值。ArtAgriculture.

在这种情况下,您没有定义 Optional 是一个枚举值,因此编译器不能使用或引用一个不存在的枚举值。

因此,它又回到了找出哪种枚举值的组合会导致(组合)的 optional 价值,并且它意识到,通过将 Subject.ArtSubject.Agriculture的值,你会得到由 optional这就是为什么它返回一个逗号分隔的字符串 Art, Agriculture.


如果你想得到逗号分隔的字符串,同时又要保留枚举本身的组成值,你将不得不自己生成逗号分隔的字符串。比如说

public string AsCommaSeparatedString(Subjects myEnum)
{
    var possibleSubjects = new List<Subjects>() { Subjects.Art, Subjects.Agriculture };

    var subjects = possibleSubjects.Where(possibleSubject => myEnum.HasFlag(possibleSubject));

    return String.Join(",", names);
}

你必须列出所有你想包含的枚举值(所以其他的值比如 Optional 将被忽略),但当你特别想排除一些值时,这是不可避免的(如 Optional)不被提及。


3
投票

你可以用下面的方式写你的枚举声明,得到同样的结果。

[Flags]
enum Subjects 
{
    Art         = 0b_0000_0001,
    Agriculture = 0b_0000_0010,
    English     = 0b_0000_0100,
    Geography   = 0b_0000_1000,
    Maths       = 0b_0001_0000,
    Science     = 0b_0010_0000,
    Optional    = 0b_0000_0011
}

编译器怎么知道 Optional 是一个组成的字段?当一个字段存在时,它将被选择在 ToString() 方法。如果您想避免这种情况,您可以删除 Optional 字段并添加一个扩展方法。

public bool IsOptional(this Subjects subjects)
 {
 return subjects.HasFlag(Subjects.Art) && subjects.HasFlag(Subjects.Agriculture);
 }

或者你也可以写你自己的方法 把你的枚举转换为字符串,也许可以使用 描述属性 来获得另一个值 Optional 领域

© www.soinside.com 2019 - 2024. All rights reserved.