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:
// 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?
You're not distinguishing between enum values and variables, but these are very different.
As an aside, I think you're abusing the purpose of an enum by trying to sneak some extra metadata about these enumvalues (i.e. whether they are optional or not) into the composed Optional
field.
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.
[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.
That leads the compiler to use the Subjects.Optional
value (and its string representation, i.e. "Optional"
) because you told the compiler that it's meaningful to you.
[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. There are only two enum values here, Art
and Agriculture
.
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.
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
.
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.