1

I want to do something like this, but I can't:

[Flags]
enum SomeEnum : int
{ 
 None = 0,
 A,
 B,
 C
}

SomeEnum en = SomeEnum.A;
en <<= 1; //Expect en == SomeEnum.B but I recieve an error here

For what It has been done so?

Tadeusz
  • 6,453
  • 9
  • 40
  • 58
  • Probably because this adds complexity to the compiler with very little benefit, when taking testing, documentation etc into account. – Oded Oct 17 '11 at 10:21
  • That looks like the kind of thing that would just make things so much harder to read. Can you give an example of when you might want to do something like that? – Chris Oct 17 '11 at 10:26
  • You can cast your enum to an int before doing any logic that would never be used on an enum since an enum is simply not ment for that. anyways: en = (SomeEnum)((int)en << 1); To clarify, take the definition according to wiki: In mathematics and theoretical computer science, the broadest and most abstract definition of an enumeration of a set is an exact listing of all of its elements – Polity Oct 17 '11 at 10:28
  • One reason this isn't allowed is that the shift operation could in theory generate a value that isn't valid for the enumeration. Besides the fact that << is an int operation. – Security Hound Oct 17 '11 at 10:55

3 Answers3

6

Enums are not bit fields in themselves. The whole point of enums is to abstract away any underlying values. Using shift operators implies knowing what these values are.

However, if you really want to shift, just cast:

en = (SomeEnum)((int)en << 1);
GazTheDestroyer
  • 20,722
  • 9
  • 70
  • 103
2

A [Flag] enum can hold a combination of flags. Shifting a combination will rarely result in sensible meaning

SomeEnum en = SomeEnum.A | SomeEnum.B;
en <<= 1; //Expect what?

You can cast to int, and do what you like if you know what you're doing

NOTE: if you really want to use SomeEnum without explicitely casting, you could wrap it with a type like Pseudo<SomeEnum> which I defined here: Is it possible to wrap integer and call it like integer?

Note that it is really a proof of concept thing, but you can see how it would work from there

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Added info about wrapping the enum with a type hat does implicit conversions to avoid casting – sehe Oct 17 '11 at 10:40
  • Technically he would expect `SomeEnum.B | SomeEnum.C`. – Jonathan Dickinson Oct 17 '11 at 10:42
  • @JonathanDickinson: the thing is, (a) we don't know what he expects (b) this could be a very good reason why the compiler doesn't support it – sehe Oct 17 '11 at 10:44
  • agreed, however, from the perspective of someone who does know what they want (e.g. as a poor example, maybe it's a permissions enum, where increasing values generally represents increasing status). You can do some crazy clever stuff with bit field math - and you never know; someone who does know what they expect might come and read your answer. – Jonathan Dickinson Oct 17 '11 at 10:50
  • @JonathanDickinson: I think you struck a poor example. That example would work well with `int` and simple increments. _Especially_ with permission maps, you don't want integer shift semantics (you want to get ever more permission (OR), or the 'execute' permission to stay unmodified, while increasing some other permissions...) – sehe Oct 17 '11 at 10:57
  • The exact reason I said 'poor example'. – Jonathan Dickinson Oct 17 '11 at 11:09
0

I would say that this is because you "Cannot apply operator '<<=' to operands of type 'SomeEnum' and 'int'". So instead you have to do the rather less elegant,

var en = SomeEnum.A;

en = (SomeEnum)((int)en << 1);

Which explicitly does the type conversion or, if you prefer you can wait for <<= to be implmented or for the Enum implicit cast to int operator to implemented, Neither of which seems likely, its not somthing that should be done by accident.

You could write you own extension method to achieve the same but I think that would need to use some reflection to handle the different base types of Enum

Jodrell
  • 34,946
  • 5
  • 87
  • 124