简体   繁体   English

为什么在枚举中声明位域的组合与在枚举之外声明它会产生不同的结果?

[英]Why does declaring a combination of bit fields inside an enum produce a different result than declaring it outside of the enum?

Here, I have a list of subjects indicated by bit fields with an "Optional" field containing the optional subjects at the bottom.在这里,我有一个由位字段指示的主题列表,其中包含底部的可选主题的“可选”字段。

[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,
}

When I print the optional subjects to the console, I am given an unexpected result:当我将可选主题打印到控制台时,我得到了一个意想不到的结果:

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

Now, if I were to declare the same Optional field outside of the enum and log it:现在,如果我要在枚举之外声明相同的 Optional 字段并记录它:

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

It works as expected.它按预期工作。

So my question is, why do I receive a different output when I place the combined bit fields in the enum vs putting it outside the enum?所以我的问题是,当我将组合位字段放在枚举中而不是将其放在枚举之外时,为什么我会收到不同的 output?

You could have written your enum declaration the following way, giving the same results:您可以通过以下方式编写枚举声明,并给出相同的结果:

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

How should the compiler know that Optional is a composed field?编译器应该如何知道Optional是一个组合字段? When a field exists, it will be chosen in the ToString() method.当一个字段存在时,它将在ToString()方法中被选中。 If you want to avoid that, you can either remove the Optional field and add an extension method:如果您想避免这种情况,您可以删除Optional字段并添加扩展方法:

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

Or you can write your own method that converts your enum to a string, maybe using the description attribute to get another value for the Optional field或者您可以编写自己的方法将您的枚举转换为字符串,也许使用description 属性来获取Optional字段的另一个值

You're not distinguishing between enum values and variables, but these are very different.您没有区分枚举值和变量,但它们非常不同。


Enum abuse枚举滥用

As an aside, I think you're abusing the purpose of an enum by trying to sneak some extra metadata about these enumvalues (ie whether they are optional or not) into the composed Optional field.顺便说一句,我认为您通过尝试将有关这些枚举值的一些额外元数据(即它们是否是可选的)潜入组合的Optional字段中来滥用枚举的目的。

I suspect the best solution for you is to move away from using the enum entirely, since enum values shouldn't have more metada surrounding them.我怀疑对您来说最好的解决方案是完全放弃使用枚举,因为枚举值不应该有更多的元数据围绕它们。

I have still answered the question as my suspicion of enum abuse is solely based on a name and my interpretation of its meaning to you.我仍然回答了这个问题,因为我对枚举滥用的怀疑完全是基于一个名字和我对你的含义的解释。 It's up to you to decide whether you're trying to sneak some metadata in the enum or whether I misunderstood your intention.由您决定是否尝试在枚举中隐藏一些元数据,或者我是否误解了您的意图。


Enum values枚举值

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

When you include the composed value in the enum, you define it as a valid enum value.当您在枚举中包含组合值时,您将其定义为有效的枚举值。 You are literally telling the compiler that Subjects.Optional is a valid (and thus meaningful) value of the enum, implying that this can and should be used.您实际上是在告诉编译器Subjects.Optional是枚举的有效(因此有意义)值,这意味着可以并且应该使用它。

That leads the compiler to use the Subjects.Optional value (and its string representation, ie "Optional" ) because you told the compiler that it's meaningful to you.这导致编译器使用Subjects.Optional值(及其字符串表示,即"Optional" ),因为您告诉编译器它对您有意义。

Variables变量

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

var optional = Subjects.Art | Subjects.Agriculture;

It's important to realize there that optional is a variable and not an enum value.重要的是要意识到optional是一个变量而不是枚举值。 There are only two enum values here, Art and Agriculture .这里只有两个枚举值, ArtAgriculture

In this case, you did not define Optional to be an enum value, and therefore the compiler cannot use or refer to an enum value that doesn't exist.在这种情况下,您没有将Optional定义为枚举值,因此编译器无法使用或引用不存在的枚举值。

Therefore, it falls back on figuring out which combination of enum values would result in the (combined) optional value, and it realizes that by combining Subject.Art and Subject.Agriculture , you get the value described by optional , which is why it returns a comma-separated string Art, Agriculture .因此,它依赖于找出枚举值的哪个组合将导致(组合的) optional值,并且它意识到通过组合Subject.ArtSubject.Agriculture ,您可以获得由optional描述的值,这就是它返回的原因逗号分隔的字符串Art, Agriculture


If you want to get the comma-separated string while also retaining the composed values in the enum itself, you're going to have to generate the comma-separated string yourself.如果您想获得逗号分隔的字符串,同时还保留枚举本身中的组合值,您将不得不自己生成逗号分隔的字符串。 For example:例如:

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

You'll have to list all the enums values which you want to include (so others like Optional will be ignored), but that's unavoidable when you specifically want to exclude some values (like Optional ) from being mentioned.您必须列出要包含的所有枚举值(因此Optional等其他值将被忽略),但当您特别想排除某些值(如Optional )时,这是不可避免的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM