23

I just need to be able to cast an object to nullable enum. Object can be enum, null, or int. Thanks!

public enum MyEnum { A, B }
void Put(object value)
{
    System.Nullable<Myenum> val = (System.Nullable<MyEnum>)value;
}

Put(null);     // works
Put(Myenum.B); // works
Put(1);        // Invalid cast exception!!
digEmAll
  • 56,430
  • 9
  • 115
  • 140
dlsou
  • 635
  • 2
  • 8
  • 18
  • You'll save yourself some trouble if you use strongly-typed declarations. If you know that `Put` expects a `Nullable`, why are you declaring it with an `object`? – Ilya Kogan Mar 04 '11 at 21:34
  • 2
    @Ilya Kogan, that's a simplified version of the Put function, it deals with other data types, not just enum – dlsou Mar 04 '11 at 21:38

4 Answers4

54

How about:

MyEnum? val = value == null ? (MyEnum?) null : (MyEnum) value;

The cast from boxed int to MyEnum (if value is non-null) and then use the implicit conversion from MyEnum to Nullable<MyEnum>.

That's okay, because you're allowed to unbox from the boxed form of an enum to its underlying type, or vice versa.

I believe this is actually a conversion which isn't guaranteed to work by the C# spec, but is guaranteed to work by the CLI spec. So as long as you're running your C# code on a CLI implementation (which you will be :) you'll be fine.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 6
    +1 I swear on a daily basis I take guidance from a Jon Skeet response on StackOverflow. Well done sir. – JustinMichaels Apr 16 '13 at 17:59
  • 1
    Shorter way: `MyEnum? val = (MyEnum?) value ?? null` – Aesir Oct 25 '17 at 16:16
  • How about: `MyEnum? val = value as MyEnum;` – mikev2 Jan 31 '18 at 12:31
  • @mikev2: That definitely behaves differently to my code, on the grounds that it will just return null if `value` isn't either an `int` or a `MyEnum`. It's hard to know whether the OP wanted that or not though. (You'd also need `value as MyEnum?;` rather than `value as MyEnum`.) – Jon Skeet Jan 31 '18 at 12:42
  • @mikev2: Additionally, `value as MyEnum?` returns null when `value` is a boxed int, unlike my version. – Jon Skeet Jan 31 '18 at 12:43
  • I _think_ I see -> it only really works as I'd intended when dealing with an object containing an int. (with the `as MyEnum?` correction) – mikev2 Jan 31 '18 at 12:57
  • @mikev2: No, it doesn't work even then - `value as MyEnum?` returns null for a boxed int, whereas the OP wants it to return a non-null `MyEnum?` value in that case. – Jon Skeet Jan 31 '18 at 12:59
  • I've just tried this out every which way and it just fundamentally completely doesn't work, when you start with anything that isn't already the enum (and then pointless). Thanks for putting me straight! – mikev2 Jan 31 '18 at 13:39
  • 1
    @mikev2: You mean using "as"? Yes, indeed. Whereas the cast *does* work. – Jon Skeet Jan 31 '18 at 13:42
  • @JonSkeet I've tried something like that earlier, and when I deployed code, build failed, and still I don't understand why, here is my code: `MyNullableEnum = product.type.HasValue ? (MyNullableEnum)product.type : null`. Thanks mate! – Roxy'Pro Feb 19 '21 at 09:32
  • @Roxy'Pro: Please *carefully* compare your code with the code in my answer. – Jon Skeet Feb 19 '21 at 09:45
  • @JonSkeet I know your code works, and when I edited my code to be like yours everything works, and I'm trying to understand why my code didn't work. I just said 'otherwise' be null `: null ` and that was the issue. But I'm not sure why since my enum is nullable it should accepts null as a value ? Thanks Jon – Roxy'Pro Feb 19 '21 at 12:13
  • @Roxy'Pro: The nullable enum type does allow a null value... but the expression `product.type.HasValue ? (MyNullableEnum)product.type : null` is invalid by the language rules of C#. The variable or property you're trying to assign the result to is not involved in inferring the type of the conditional expression. If you want a lot more detail, ask a new question - the detailed answer would be long and refer to specific bits of the C# spec. – Jon Skeet Feb 19 '21 at 12:19
13

This is because you're unboxing and casting in a single operation, which is not allowed. You can only unbox a type to the same type that is boxed inside of the object.

For details, I recommend reading Eric Lippert's blog: Representation and Identity.

Julian
  • 20,008
  • 17
  • 77
  • 108
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 2
    Actually, not quite. See my answer - you *can* unbox from a boxed `int` to an enum type (with `int` as the underlying type), or from a boxed enum value to its underlying type. – Jon Skeet Mar 04 '11 at 21:31
  • @Jon: True - but that's also somewhat an edge case (only works because the "enum" is actually an "int" in this specific case) - what happens when the OP passes 0.0 into the method? Or they try to use this technique with an enum backed by something other than Int32? Those will still cause a problem... – Reed Copsey Mar 04 '11 at 21:35
  • 1
    Then it will break. But the OP does say: "Object can be enum, null, or int." In other words, the solution I've given works for the problem as presented. We don't know what behaviour is desired in other situations. – Jon Skeet Mar 04 '11 at 21:39
4

a bit offtopic

For Those, who needs the result to be null when the value is not defined in the enum;

One should use either :

typeof(MyEnum).IsEnumDefined(val)

or

Enum.IsDefined(typeof(MyEnum), val)

Here is the usage example:

enum MyEnum { one = 1, three = 3, four=4 }

static MyEnum? CastIntAsNullableEnum(int value) 
{
    if (!Enum.IsDefined(typeof(MyEnum), value)) return null;
    return (MyEnum?)value;            
}

static void CastIntAsNullableEnumTest()
{
    for(int i =0; i<=5; i++)
    {
        Console.WriteLine("converted {0} is {1}", i, CastIntAsNullableEnum(i));
    }                        
}

Thanks to John Thiriet (johnthiriet.com) for the solution

Pasetchnik
  • 318
  • 1
  • 9
1

When you are assigning a value to a nullable type you have to be aware that it is not the same as the underlying type(at least in this case). So in order to perform the cast you need to unbox first:

void Put(object value)
{
    if (value != null)
    {
        System.Nullable<Myenum> val = (System.Nullable<MyEnum>)(MyEnum)value;
    }
}
TheBoyan
  • 6,802
  • 3
  • 45
  • 61