简体   繁体   中英

& operator with enum values in C#

I have following code and Console.WriteLine is returning Bottom even though Bottom is not in both enum expressions.

Question

What is the logic behind returning Bottom in code snippet given below? My understanding of & operator is that it returns the common part, but in this case there is nothing common between the two enum expressions.

void Main()
{
    Console.WriteLine(( Orientations.Left | Orientations.Bottom) & 
                     (Orientations.Right| Orientations.Top));//returns Bottom
}


[Flags]
public enum Orientations {
Left = 0, Right= 1, Top=2, Bottom =3
};

You assign values to the enums, and the operators | and & work on the enum values, like they would work on the corresponding values.

You have set the values of the enum values yourself, and you have not set them orthogonal . Since integers are in fact bitstrings (with fixed length), you can see it as a 32-dimensional vector (with every vector element having domain {0,1} ). Since you defined for instance Bottom as 3 , it means that Bottom is actually equal to Right | Top Right | Top , since:

Right | Top
    1 |   2  (integer value)
   01 |  10  (bitwise representation)
   11        (taking the bitwise or)
Bottom

So that means that if you write & , this is a bitwise AND , and | , is a bitwise OR on the values of the enum values.

So if we now evaluate it, we get:

(Orientations.Left|Orientations.Bottom) & (Orientations.Right|Orientations.Top)
(0                | 3                 ) & (1                 | 2)
3                                       & 3
3
Orientations.Bottom

If you want to define four orthogonal values , you need to use powers of two :

[Flags]
public enum Orientations {
    Left = ,    // 0001
    Right = ,   // 0010
    Top = ,     // 0100
    Bottom =    // 1000
};

Now you can see the enum as four different flags, and and the & will create the intersection, and | the union of the flags. In comment the bitwise representation of each value is written.

As you can see, we can now see Left , Right , Top and Bottom as independent elements, since we can not find a monotonic bitwise construction ( to combine Left , Right and Top to construct Bottom (except negation).

In order for flag enums to work as expected, the enum constants need to be powers of 2.

In your example the binary values look like this (I show 4 bits only for sake of simplicity)

Left   = 0                     0000
Right  = 1                     0001
Top    = 2                     0010
Bottom = 3                     0011
Left | Right | Top | Bottom =  0011 which is 3 or Bottom again 

If you choose powers of 2 you get

Left   = 1 = 2^0               0001
Right  = 2 = 2^1               0010
Top    = 4 = 2^2               0100
Bottom = 8 = 2^3               1000
Left | Right | Top | Bottom =  1111 

Ie, with powers of 2, different bits are set and therefore they combine neatly with the bitwise OR operator (|).

Since C# 7.0 you can use binary literals

[Flags]
public enum Orientations {
    Left   = 0b0001,
    Right  = 0b0010,
    Top    = 0b0100,
    Bottom = 0b1000
};

In previous versions of C# you can also use the left shift operator to get powers of 2

[Flags]
public enum Orientations {
    Left   = 1 << 0,
    Right  = 1 << 1,
    Top    = 1 << 2,
    Bottom = 1 << 3
};

It is a good practice to also include the enum constant None = 0 because enum fields are initialized to default(MyEnum) == 0 , otherwise resulting in a value having no corresponding enum constant.

You can also create new combined enum values like this

[Flags]
public enum Orientations {
    None   = 0,
    Left   = 1 << 0,
    Right  = 1 << 1,
    Top    = 1 << 2,
    Bottom = 1 << 3,
    Horizontal = Left | Right,
    Vertical = Top | Bottom,
    All = Horizontal | Vertical
};

Note that every enum has an implicit conversion from 0. Therefore you could do this test

if((myOrientations & Orientations.Vertical) != 0) {
    // We have at least a Top or Bottom orientation or both
}

It is & and | bitwise operation. In the example:

(( Orientations.Left | Orientations.Bottom) & 
                     (Orientations.Right| Orientations.Top))

Will replace with

((0 | 3) & (1 | 2)) with in bit (show only last 3 bit):
((000 |011) & (001 | 010))
= (011 & 011)
= 011

011 is 3 in int value which is Orientations.Bottom value. Therefore, It is always returning Orientations.Bottom.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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