Non-Unique Enum Values

Non-unique enum values

Actually you're already defining a struct... Behind the scenes an enum is just a struct (but which derives from System.Enum) and the values of the enum are defined as constants (you can verify this with ILDASM).

Your enum definition translates into the following pseudo C# code:

public struct Color : System.Enum
{
public const int Red = 1;
public const int Blue = 1;
public const int Green = 1;
}

The above code won't compile in C# because the compiler doesn't allow defining a struct with an explicit base class, but that's what it emits for an enum definition.

Since there is no problem with a type that contains an multiple constants that have the same value, there is no problem with the enum definition.

But since the enum does not have unique values you might have an issue when converting into this enum.
For example the following two line of codes will return the enum value Red, because the first value is arbitrarily selected.

Color color1 = (Color)1;
Color color2 = (Color)Enum.Parse(typeof(Color), "1");

Strictly speaking the enum value is not Red, it is 1, but when you print out the value you'll see Red.

Also, the following boolean is true which looks a bit weird...

// true (Red is Green??)
bool b = Color.Red == Color.Green;

At the bottom line this is perfectly legal, but it's up to you to use it when it makes sense...

Here is a direct link to the section of my .NET tutorial that discusses enumerations under the hood: http://motti.me/c1E

Loop through all enum values with multiple non unique numbers

You can obtain all the enums names and filter those which match the value

Code:

private static string LongName<T>(long value) where T : Enum {
unchecked {
return string.Join(" or ", Enum
.GetNames(typeof(T))
.Where(name => Convert.ToInt64(Enum.Parse(typeof(MyEnum), name)) == value)
.OrderBy(name => name));
}
}

private static string LongAttributeName<T>(long value) where T : Enum {
unchecked {
return string.Join(" or ", Enum
.GetNames(typeof(T))
.Where(name => Convert.ToInt64(Enum.Parse(typeof(MyEnum), name)) == value)
.Select(name => typeof(T)
.GetMember(name)
.FirstOrDefault(mi => mi.DeclaringType == typeof(T)))
.SelectMany(mi => mi.GetCustomAttributes<CategoryEnumAttribute>(false))
.Select(attr => attr.Description) //TODO: Put the right property here
.OrderBy(name => name));
}
}

private static string[] LongNames<T>() where T : Enum {
unchecked {
return Enum
.GetNames(typeof(T))
.OrderBy(name => Convert.ToInt64(Enum.Parse(typeof(MyEnum), name)))
.ThenBy(name => name)
.ToArray();
}
}

private static string[] LongNamesCombined<T>() where T : Enum {
unchecked {
return Enum
.GetNames(typeof(T))
.GroupBy(name => Convert.ToInt64(Enum.Parse(typeof(MyEnum), name)))
.OrderBy(group => group.Key)
.Select(group => string.Join(" or ", group.OrderBy(name => name)))
.ToArray();
}
}

Demo:

// Single Value
Console.Write(LongName<MyEnum>(1));
// Single Value attributes
Console.Write(LongAttributeName<MyEnum>(1));
// All Values (array with 4 items)
Console.Write(string.Join(", ", LongNames<MyEnum>()));
// All Values combined (array with 2 items)
Console.Write(string.Join(", ", LongNamesCombined<MyEnum>()));

Outcome:

Red or Strong
Color or Attributes # <- Depends on implementation
Red, Strong, Blue, Weak
Red or Strong, Blue or Weak

Python Enums with duplicate values

Yes, labels with duplicate values are turned into aliases for the first such label.

You can enumerate over the __members__ attribute, it is an ordered dictionary with the aliases included:

>>> for name, value in CardNumber.__members__.items():
... print(name, value)
...
ACE CardNumber.ACE
TWO CardNumber.TWO
THREE CardNumber.THREE
FOUR CardNumber.FOUR
FIVE CardNumber.FIVE
SIX CardNumber.SIX
SEVEN CardNumber.SEVEN
EIGHT CardNumber.EIGHT
NINE CardNumber.NINE
TEN CardNumber.TEN
JACK CardNumber.TEN
QUEEN CardNumber.TEN
KING CardNumber.TEN

However, if you must have label-and-value pairs that are unique (and not aliases), then enum.Enum is the wrong approach here; it doesn't match the usecases for a card game.

In that case it'll be better to use a dictionary (consider using collections.OrderedDict() if order is important too).

Should the Enum values be unique in the global scope?

What you are talking about is behaviour of the c language.

In c# enums are not globally accessible by name, you always have to access them by typeName.valueName, e.g. color.Orange, fruit.Orange. So there will be no conflict. In fact many in-built enums use the same value names (StringSplitOptions.None, CompareOptions.None).

How to prevent duplicate values in enum?

This isn't prohibited by the language specification, so any conformant C# compiler should allow it. You could always adapt the Mono compiler to forbid it - but frankly it would be simpler to write a unit test to scan your assemblies for enums and enforce it that way.

c++: ensure enum values are unique at compile time

I'm not sure if Boost is available in your scenario, so here is a solution where the enum has to be defined in a pre-processor sequence. That sequence is then used to build the enum and a corresponding mpl::vector and we compute if the elements of the vector are unique in an odd-fashion. We might want to define a proper is_unique algorithm first, but this should do.

#include <boost/mpl/vector.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/sort.hpp>
#include <boost/mpl/unique.hpp>
#include <boost/mpl/size.hpp>

#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/transform.hpp>


#define MYENUM ((FOO, 0))((BAR, 1))((BAZ, 2))

#define GET_NAME(_, __, elem) BOOST_PP_TUPLE_ELEM(2, 0, elem) = BOOST_PP_TUPLE_ELEM(2, 1, elem)
#define GET_VALUE(_, __, elem) boost::mpl::int_<BOOST_PP_TUPLE_ELEM(2, 1, elem)>

enum E {
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(GET_NAME, _, MYENUM))
};

typedef boost::mpl::sort<
boost::mpl::vector<
BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(GET_VALUE, _, MYENUM))
>
>::type evalues;

typedef boost::mpl::unique< evalues, boost::is_same<boost::mpl::_1, boost::mpl::_2> >::type uniqued;
static_assert(boost::mpl::size<uniqued>::value == boost::mpl::size<evalues>::value, "enum values not unique");

int main()
{

return 0;
}

If you change the enum definition to:

#define MYENUM ((FOO, 0))((BAR, 1))((BAZ, 2))((BAZZ, 2))

you will get an error stating the static_assert failed "enum values not unique".

Duplicate string values in TS enum does not cause compilation error?

regarding the TS ENUM specification:

Enums allow us to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases. TypeScript provides both numeric and string-based enums.

There is nothing about that it should to be uniq, so probably that behaivor is okey.

enum/typescript

UPDATE:
There is another interesting thing about the ENUM and 'bugs':

Enum value incrementation does not consider previously defined values, nor does the compiler throws an error on duplicate values.

Which means you can end up with potential bugs:

enum Color {Red = 3, Green = 2, Blue};

Color.Red == Color.Blue; //true

C++11 Enum class containing duplicate values

There is currently no way to detect or prevent multiple identical enum values in an enum.

The reflection working group is working on how to add reflection -- the ability for C++ code to introspect C++ code -- to the language. In the long list of stuff that reflection covers, there is a short list being worked on, and in that short list, examining the values of an enumeration at compile time is there.

N4428 contains a proposal for enum reflection. There are some partial implementations out there.

Under N4428, detecting duplicates would be easy. You can get the number of enumeration values, and their value, all at compile time. Simply create a pack of all the enum values in order, and test that they are unique. Then toss the result of that test into a static_assert.

The end result could be:

template<class E>
constexpr bool all_values_unique(); // todo

static_assert( all_values_unique<ConnectionState>(), "Duplicate enum value detected" );

Prior to something like that reflection proposal being added to C++, this is not possible.

You can fake it using macros -- have a macro that both creates your enum and creates reflection traits information about it -- then write all_values_unique that uses the reflection traits information. This has the advantage that if/when the standard and/or your compiler gets the reflection features needed, it may be easy to strip out your macros.



Related Topics



Leave a reply



Submit