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



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

[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"

它按预期工作。

所以我的问题是,为什么当我将组合位字段放在枚举中时,我会收到不同的输出,而不是将其放在枚举之外?

你可以用以下方式编写枚举声明,给出相同的结果:

[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);
}

或者你可以编写自己的方法将枚举转换为字符串,也许使用 description 属性来获取Optional字段的另一个值

您没有区分枚举值和变量,但它们非常不同。


枚举滥用

顺便说一句,我认为您滥用枚举的目的,试图将有关这些枚举值的一些额外元数据(即它们是否可选(潜入组合Optional字段中。

我怀疑最适合您的解决方案是完全不使用枚举,因为枚举值不应该有更多的元围绕它们。

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


枚举值

[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(时,这是不可避免的。

最新更新