0

I want to get an enum as an int, but only if it has an initializer applied to it - so that I know that the value was explicitly defined and isn't just the default value. This is because I am using enums to represent a mapping with defined int codes. Is there a way to do this with enums, or am I going to have to use something else like a dictionary?

Here is an example - I have the following mapping:

Apples = 1, Bananas = 2, Pears = 4, Pineapples = 7

I want to make sure that if the enum is

public enum Fruit
{
Apples,
Bananas,
Pears,
Pineapples
}

that it won't try to get the int value, because a value of 1 in the enum corresponds to Bananas, not Apples, like it should. Let me know if I need to be more clear.

UPDATE:

I use nullable enums to check if the value was set (or an Unknown element), but that's not what I'm asking here. I don't think I'm doing a very good job explaining it so I'll try again.

I want to be able to get an enum that I know nothing about, take the underlying int and then do something with it. The only thing I care about is that the enum was initialized because it tells me that it has actual codes defined on it. I don't mind if it's null. That is one of the things I check for. But if it isn't null, I want to make sure that the int code is explicitly defined. A code of 0 is totally acceptable - even if it means that it is undefined - as long as that is what the original mapping specifies.

ANOTHER UPDATE:

I got this term 'initialized' from the Microsoft documentation and I think it is causing confusion. What I meant was an enum that has the value of the underlying ints explicitly defined on it. I.e.,

public enum MyEnum
{
One = 1,
Two = 2,
Three = 3
}

as opposed to one that doesn't. I.e.,

public enum MyEnum
{
One,
Two,
Three
}

I think it was also causing confusion that I was referring to these as having the default value set, which is easy to confuse with non-nullable enums that have the first element set by default.

The way that I'm using this is that I have a method that uses reflection to get the value of properties that have certain custom attributes on them. For my purposes, I only want to get the value from enums that have codes explicitly defined on them. Otherwise I can't assume that the int value has any business meaning.

lintmouse
  • 5,079
  • 8
  • 38
  • 54
  • 3
    I always set a `Unknown=0` value in my Enum will be serialized so I can always know that 0 = undefined. – asawyer Apr 02 '13 at 20:56
  • @asawyer good idea, I always start the first value at 1. – usr Apr 02 '13 at 20:57
  • It's also a good hint to other dev's that this enum can possibly have uninitialized values, as opposed to enums that never will. – asawyer Apr 02 '13 at 20:59

3 Answers3

1

At the IL level, an enum is just an integer (unless it is boxed); you cannot tell an implicit 0 from an explicit 0. A Nullable<SomeType> (aka SomeType?) might be a workable idea though. Other than that: you'll have to come up with some local definition of what a zero vs default it.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • I think you answered my question. I really just wanted to know if it was possible to distinguish between initialized and default int values on enums. I think they could support it if they wanted to. Doesn't seem to be much demand for it - besides from me, haha. – lintmouse Apr 02 '13 at 21:30
  • @dustmouse *how* could they support it, in your view? An enum can be any integer value (so there are no sentinel values left). To do that the implementation of enums would have to be very different – Marc Gravell Apr 03 '13 at 06:25
  • Yeah, the underlying implementation would be different. It would require a little more storage to keep track of whether the int was initialized or default. Why couldn't they accomplish that? (I'm not saying they should or that I really want that.) – lintmouse Apr 03 '13 at 13:42
0

Enums are actually ordinal value types underneath, so they cannot be null, and similar solution cannot be applied.

Best solution is to add enum entry which would correspond to value 0. E.g.

public enum Fruit
{
    Unknown = 0,
    Apples,
    Bananas,
    Pears,
    Pineapples
}

Fruit someFruit;
if (someFruit == Fruit.Unknown)
    Console.WriteLine("fruit is not initialized");
else
    Console.WriteLine("fruit is initialized");

Another way to approach it would be to wrap a value into a Nullable. This way you would allow a variable to be set to null.

public enum Fruit
{
    Apples,
    Bananas,
    Pears,
    Pineapples
}

Fruit? someFruit;
if (!someFruit.HasValue)
    Console.WriteLine("fruit is not initialized");
else
    Console.WriteLine("fruit is initialized");
Nikola Radosavljević
  • 6,871
  • 32
  • 44
0

A (non-nullable) enum value is always a valid integer. Therefore it is possible to get values that are not used by any of the constants in the enum:

Fruit myFruit = (Fruit)12345;    // Some random integer

And the other way around: you can always get the integer value of any enum, regardless of whether its value is defined as a constant or not.

Fruit myFruit = (Fruit)12345;
int myFruitInt = (int)myFruit;

Therefore, if you want to be sure the value of the enum is one of the values defined in the enum's definition, you can use the Enum.IsDefined method, like this:

Fruit myFruit;
bool isDefined;

myFruit = Fruit.Bananas;
isDefined = Enum.IsDefined(typeof(Fruit), myFruit); // true, Bananas

myFruit = (Fruit)1;
isDefined = Enum.IsDefined(typeof(Fruit), myFruit); // true, Bananas

myFruit = Fruit.Bananas | Fruit.Pineapples;
isDefined = Enum.IsDefined(typeof(Fruit), myFruit); // false

myFruit = (Fruit)12345;
isDefined = Enum.IsDefined(typeof(Fruit), myFruit); // false

Of course, you can put any Type in the method, not just typeof(Fruit).


You talk about initialized enums. There is no such thing. An enum can have any integer value. The default value for value types (including enums) is 0. But it is entirely possible (and even recommended) that there is a enum constant with a value of 0 (usually named None, Unknown or Default). So, the following are semantically exactly the same:

private Fruit myFruit;

private Fruit myFruit = (Fruit)0;

private Fruit myFruit = Fruit.Unknown;

You cannot tell the difference at run-time, because there is no difference.


Apparently the MSDN documentation on enum states:

By default, the first enumerator has the value 0, and the value of each successive enumerator is increased by 1. For example, in the following enumeration, Sat is 0, Sun is 1, Mon is 2, and so forth.

enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri};

Enumerators can use initializers to override the default values, as shown in the following example.

enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

In this enumeration, the sequence of elements is forced to start from 1 instead of 0. However, including a constant that has the value of 0 is recommended.

I'm not sure wht the documentation calls these initializers, since there is nothing initialized. It is just syntax to make your life easier. You could just as well have specified the equivalent:

enum Days {Sat=1, Sun=2, Mon=3, Tue=4, Wed=5, Thu=6, Fri=7};

Perhaps they mean that 1 is the initial value from which the compiler will start counting to assign a constant integer value to the enum constants that follow it. For example:

enum Days {Sat, Sun=2, Mon, Tue=10, Wed, Thu, Fri=11};

Is equivalent to:

enum Days {Sat=0, Sun=2, Mon=3, Tue=10, Wed=11, Thu=12, Fri=11};

Again, nothing is initialized.

Daniel A.A. Pelsmaeker
  • 47,471
  • 20
  • 111
  • 157
  • In my case, I won't be trying to get an element by its int. I will be getting the int for the element. I just want to know if it's possible to tell if the int is initialized or default. – lintmouse Apr 02 '13 at 21:20
  • @dustmouse You talk about _initialized_ but there is no such thing. Please see my update. – Daniel A.A. Pelsmaeker Apr 02 '13 at 21:22
  • Check out this link. It talks about initialized enums near the beginning: http://msdn.microsoft.com/en-us/library/sbbt4032(v=vs.71).aspx – lintmouse Apr 02 '13 at 21:25
  • @dustmouse That's an mishap in the documentation. _Nothing is initialized._ Instead, they mean the _integer value_ assigned to each enum constant _at compile-time_. By default enum constants get a value starting at 0. You can give an enum constant any explicit integer value, and the following constants are implicitly assigned the successive integer values. – Daniel A.A. Pelsmaeker Apr 02 '13 at 21:30
  • @dustmouse I added it to my post. – Daniel A.A. Pelsmaeker Apr 02 '13 at 21:37
  • I wasn't hung up on the term 'initialized', just the fact that the values can be default or explicitly defined and whether it was possible to distinguish between them at runtime. – lintmouse Apr 02 '13 at 21:49
  • @dustmouse If an enum has its default value, its value is 0. That's easy. If an enum's value is explicitly defined, `IsDefined` returns `true` as I explain above. It is possible for its default value to be explicitly defined, for which `Enum.IsDefined(typeof(Fruit), (Fruit)0)` returns `true`. – Daniel A.A. Pelsmaeker Apr 02 '13 at 21:51
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/27438/discussion-between-dustmouse-and-virtlink) – lintmouse Apr 02 '13 at 21:52