0

I have following enum with flags attribute:

[Flags]
public enum MyEnum
{
    None = 0,
    First = 1,
    Second= 2,
    Third= 4,
       .
       .
       .
    Unknown = All >> 1,
    All = ~None,
}

and I want the Unknown value to be the last before the All value. So I thought that the right-shift operator will do the work, but when I print both of the values to console like this:

Console.WriteLine((int)MyEnum.All);
Console.WriteLine((int)MyEnum.Unknown);

it prints -1 and -1. What am I doing wrong and how can I modify the code to have Unknown right before All?

Edit: thanks to everyone for the asnwers, I overlooked that the right-shift operator performs arithmetic shift and "propagates the sign bit to the high-order empty bit positions". In the end I realized that my approach would give me wrong results anyway because MyEnum.Unknown.HasFlag(MyEnum.First); would return true and that would be incorrect behavior. This answer pointed me to the right direction and I set Unknown to int.MinValue and that is excatly what I wanted to accomplish - I wanted Unknown to be like any other value and also be the highest possible one. Now Im just embarrassed that I did not think about that earlier...

Marmellad
  • 301
  • 2
  • 12
  • 2
    From [Right-shift operator >>](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators#right-shift-operator-): "If the left-hand operand is of type `int` or `long`, the right-shift operator performs an _arithmetic_ shift: the value of the most significant bit (the sign bit) of the left-hand operand is propagated to the high-order empty bit positions." – HABO Feb 09 '21 at 20:20
  • For completeness, from [Enumeration types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/enum): "By default, the associated constant values of enum members are of type `int`; they start with zero and increase by one following the definition text order." – HABO Feb 09 '21 at 20:22
  • `Unknown = (int)unchecked(((uint)All) >> 1),` – Dmitry Bychenko Feb 09 '21 at 20:58

5 Answers5

3

It appears that you want All to be 0xFFFFFFFF and Unknown to be 0x8000000 so that Unknown is included in All. Interesting idea. Perhaps 0x7FFFFFFF and 0x8000000 would make more sense:

using System;
                    
public class Program
{
    [Flags]
    public enum MyEnum : uint
    {
    None = 0,
    First = 1,
    Second= 2,
    Third= 4, // ...
    Unknown = (uint)1 << 31, // Most-significant-bit.
    All = ~Unknown}

    public static void Main()
    {
        Console.WriteLine("All: " + ((uint)MyEnum.All).ToString("X"));
        Console.WriteLine("Unknown: " + ((uint)MyEnum.Unknown).ToString("X"));
    }
}

.NETfiddle.

HABO
  • 15,314
  • 5
  • 39
  • 57
0

Enumerations are based on signed integers, namely int.

If you play with any integers, you'll that this behaviour is totally correct (output from C# interactive):

> int i = 0;
> int j = ~i;
> j
-1
> int k = j >> 1;
> k
-1

For more depth explanation and understanding I recommend reading through this SO post. It has even answer from one of a guys working on C# language :)

But hte baseline is: what you are seeing is totally correct and works that way because enums are ints underneath, unless you specify otherwise (you can use uint instead of long):

public enum LongEnum : long
{
    //values
}
Michał Turczyn
  • 32,028
  • 14
  • 47
  • 69
  • 4
    To clarify you can override the default "int" behavior by doing this `public enum MyEnum : uint {...}` – Andy Feb 09 '21 at 20:22
  • 1
    @Andy yes, i was just writing that :) – Michał Turczyn Feb 09 '21 at 20:23
  • Hmm.. If I do this: `public enum MyEnum : uint {...}` I still get sign extension behavior (both `All` and `Unknown` end up equal to `4294967295`). Do you want the high bit to be zero? You may want to clear that bit with `All & 7FFF_FFFF`. – Flydog57 Feb 09 '21 at 20:40
0

may be it fits for you:

  0     0x00000000     None
  1     0x00000001     First
  2     0x00000002     Second
  4     0x00000004     Third
 -2     0xFFFFFFFE     Unknown
 -1     0xFFFFFFFF     All

code:

using System;

[Flags]
public enum MyEnum
{
    None = 0,
    First = 1,
    Second= 2,
    Third= 4,
    Unknown = All << 1,
    All = ~None,
}

public class Example {
    public static void Main() {
        foreach (var value in Enum.GetValues(typeof(MyEnum))) {
            Console.WriteLine("{0,3}     0x{0:X8}     {1}",
                           (int) value, ((MyEnum) value));
        }   
    
   }
}
Daniil Loban
  • 4,165
  • 1
  • 14
  • 20
0

The following may help. 'All' covers all the bits apart from the most significant bit. The most significant bit is used for 'Unknown'.

[Flags]
public enum MyEnum : uint
{
    None = 0,
    First = 1,
    Second = 2,
    Third = 4,
    All = uint.MaxValue >> 1,
    Unknown = ~All,
}
Jason Hunt
  • 354
  • 2
  • 5
0

Well, right shift >> works different for int and uint when the most significant bit is set to 1:

int  : 1.... >> 1 == 11.... : sign bit (which is 1 here) propagates
uint : 1.... >> 1 == 01.... : 0 added

You want the second type behaviour, which is demonstrated by uint, so let's cast All to uint while doing right shift:

[Flags]
public enum MyEnum
{
    None = 0,
    ...
    Unknown = (int)unchecked(((uint)All) >> 1),
    All = ~None,
}

I would not recommend public enum MyEnum : uint declaration since uint is not cls compliant.

Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215