views:

878

answers:

3

Given a generic parameter TEnum which always will be an enum type, is there any way to cast from TEnum to int without boxing/unboxing?

See this example code. This will box/unbox the value unnecessarily.

private int Foo<TEnum>(TEnum value)
    where TEnum : struct  // C# does not allow enum constraint
{
    return (int) (ValueType) value;
}

The above C# is release-mode compiled to the following IL (not boxing and unboxing opcodes):

.method public hidebysig instance int32  Foo<valuetype 
    .ctor ([mscorlib]System.ValueType) TEnum>(!!TEnum 'value') cil managed
{
  .maxstack  8
  IL_0000:  ldarg.1
  IL_0001:  box        !!TEnum
  IL_0006:  unbox.any  [mscorlib]System.Int32
  IL_000b:  ret
}

Enum conversion has been treated extensively on SO, but I could not find a discussion addressing this specific case.

+3  A: 

I guess you can always use System.Reflection.Emit to create a dynamic method and emit the instructions that do this without boxing, although it might be unverifiable.

Cecil Has a Name
Snap. BTW you can create verifiable IL using Reflection.Emit for this scenario.
Drew Noakes
+4  A: 

I'm not sure that this is possible in C# without using Reflection.Emit. If you use Reflection.Emit, you could load the value of the enum onto the stack and then treat it as though it's an int.

You have to write quite a lot of code though, so you'd want to check whether you'll really gain any performance in doing this.

I believe the equivalent IL would be:

.method public hidebysig instance int32  Foo<valuetype 
    .ctor ([mscorlib]System.ValueType) TEnum>(!!TEnum 'value') cil managed
{
  .maxstack  8
  IL_0000:  ldarg.1
  IL_000b:  ret
}

Note that this would fail if your enum derived from long (a 64 bit integer.)

EDIT

Another thought on this approach. Reflection.Emit can create the method above, but the only way you'd have of binding to it would be via a virtual call (i.e. it implements a compile-time known interface/abstract that you could call) or an indirect call (i.e. via a delegate invocation). I imagine that both of these scenarios would be slower than the overhead of boxing/unboxing anyway.

Also, don't forget that the JIT is not dumb and may take care of this for you. (EDIT see Eric Lippert's comment on the original question -- he says the jitter does not currently perform this optimisation.)

As with all performance related issues: measure, measure, measure!

Drew Noakes
Many thanks. I'll look at doing this eventually. For now I will leave the boxing conversion in place.
Jeffrey Sharp
+1  A: 

I know I'm way late to the party, but if you just need to do an safe cast like this you can use the following using Delegate.CreateDelegate::

public static int Identity(int x){return x;}
// later on..
Func<int,int> identity = Identity;
Delegate.CreateDelegate(typeof(Func<int,TEnum>),identity.Method) as Func<int,TEnum>

now without writing reflection.Emit or Expression trees you have a method that will convert int to Enum without boxing or unboxing. Note that TEnum here must have an underlying type of int or this will thow an exception saying it cannot be bound.

Michael B