ygoe
ygoe

Reputation: 20384

Is a value an enum and -1?

For view model validation, I need to determine whether a value, of which I only have an object interface, is an enum and has the numeric value of -1.

I tried this:

// object value;
if (value?.GetType().IsEnum == true && (int)value == -1) return null;

It should work with my Model enums which are mostly based on int.

But it fails when the value is a Visibility enum (that happens to be also in the view model class and should be ignored for validation) which is based on byte instead of int and that seems not to be castable to int. I could do some more testing but it shouldn't get too slow.

Is there a good simple solution for that? Maybe some test method in the Enum class or something?

Upvotes: 3

Views: 220

Answers (4)

René Vogt
René Vogt

Reputation: 43896

You can check the underlying type with GetEnumUnderlyingType():

Type t = value?.GetType();
if (t?.IsEnum == true && 
    t?.GetEnumUnderlyingType() == typeof(int) && 
    (int)value == -1)
    return null;

Since a byte can never be -1, you don't need to check it. But you may need to extend the check for long enums, too.


UPDATE:

I just tried around a little and found that Convert.ToInt32() also solves your problem:

if (value?.GetType().IsEnum == true && 
    Convert.ToInt64(value) == -1)
    return null;

This seems cleaner and also works for all possible underlying types.


Another update: Unfortunatly the solution above is not as clean as I thought. Even with Convert.ToInt64() solves the problem of long values too big for Int32, but it throws if you pass for example a ulong.MaxValue.

So you have to choose a type that is large enough for all possible enum base types:

if (value?.GetType().IsEnum == true && 
    Convert.ToDecimal(value) == -1)
    return null;

Using Convert.ToDecimal() this passes all the test cases that came up so far.

Upvotes: 6

Mikhail Tulubaev
Mikhail Tulubaev

Reputation: 4261

byte can be casted to int if it is not boxed with object

byte b = 1;
int i = (int)b; //works good
int f = (int)(object)b; //fails

So you could convert your variable to int using Convert.ToInt32 method, as René Vogt suggested, or cast it to dynamic instead casting to int:

if (value?.GetType().IsEnum == true && (dynamic)value == -1) return null;

Although, operations with dynamic are rather slow. In my opinion, soultion with Convert.ToInt32 is the most cleanable and efficient. This answer is just to point out, why you can not cast the object to int and suggest a dynamic version of cast.

Upvotes: 0

xanatos
xanatos

Reputation: 111870

You could:

if (value != null)
{
    var type = value.GetType();

    if (type.IsEnum && object.Equals(Enum.ToObject(type, -1), value))
    {
        return null;
    }
}

I had to do it in multiple lines to "cache" the GetType(). The trick is using Enum.ToObject() to "cast" -1 to the current Enum type.

Note that casting will generate "strange" results:

public enum TestEnum : ulong
{
    ValueMax = ulong.MaxValue
}

In this case, TestEnum.ValueMax will return null. This will happen for all the unsigned types.

Upvotes: 0

JensG
JensG

Reputation: 13421

In addition, one could consider using the Enum.IsDefined Method (Type, Object) to verify, if the value is a valid enum.

That does of course not cover the "check if it is -1" part.

Enum.IsDefined Method (Type, Object)

Returns an indication whether a constant with a specified value exists in a specified enumeration. Namespace: System Assembly: mscorlib (in mscorlib.dll)

[ComVisibleAttribute(true)] public static bool IsDefined( Type enumType, object value )

Parameters

enumTypeType: System.Type An enumeration type.
valueType: System.Object The value or name of a constant in enumType.

Return Value

Type: System.Boolean becomes true if a constant in enumType has a value equal to value; otherwise, false.

Exceptions

ArgumentNullException' enumType or value is null. ArgumentException` enumType is not an Enum. -or- The type of value is an enumeration, but it is not an enumeration of type enumType. -or- The type of value is not an underlying type of enumType.

InvalidOperationException value is not type SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, UInt64, or String.

Remarks

The value parameter can be any of the following:

  • Any member of type enumType.
  • A variable whose value is an enumeration member of type enumType.
  • The string representation of the name of an enumeration member. The characters in the string must have the same case as the enumeration member name.
  • A value of the underlying type of enumType.

If the constants in enumType define a set of bit fields and value contains the values, names, or underlying values of multiple bit fields, the IsDefined method returns false. In other words, for enumerations that define a set of bit fields, the method determines only whether a single bit field belongs to the enumeration. To determine whether multiple bit fields are set in an enumeration type that is tagged with the FlagsAttribute attribute, you can call the HasFlag method.

Upvotes: 0

Related Questions