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 (note 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.
I hope that I'm not too late...
I think that you should consider to solve your problem with a different approach instead of using Enums try to creating a class with a public static readonly properties.
if you will use that approach you will have an object that "feels" like an Enum but you will have all the flexibility of a class which means that you can override any of the operators.
there are other advantages like making that class a partial which will enable you to define the same enum in more then one file/dll which makes it possible to add values to a common dll without recompiling it.
I couldn't find any good reason not to take that approach (this class will be located in the heap and not on the stack which is slower but it's worth it)
please let me know what you think.
This is similar to answers posted here, but uses expression trees to emit il to cast between types.
Expression.Convert
does the trick. The compiled delegate (caster) is cached by an inner static class. Since source object can be inferred from the argument, I guess it offers cleaner call. For e.g. a generic context:The class:
You can replace the
caster
func with other implementations. I will compare performance of a few:Boxed casts:
int
toint
int
toint?
int?
toint
enum
toint
int
toenum
int?
toenum
enum?
toint
Expression.Convert
puts a direct cast from source type to target type, so it can work out explicit and implicit casts (not to mention reference casts). So this gives way for handling casting which is otherwise possible only when non-boxed (ie, in a generic method if you do(TTarget)(object)(TSource)
it will explode if it is not identity conversion (as in previous section) or reference conversion (as shown in later section)). So I will include them in tests.Non-boxed casts:
int
todouble
enum
toint?
int
toenum?
enum?
toint?
int?
toenum?
For the fun of it, I tested a few reference type conversions:
PrintStringProperty
tostring
(representation changing)string
toobject
(representation preserving reference conversion)Tested like this:
Note:
My estimate is that unless you run this at least a hundred thousand times, it's not worth it, and you have almost nothing to worry about boxing. Mind you caching delegates has a hit on memory. But beyond that limit, the speed improvement is significant, especially when it comes to casting involving nullables.
But the real advantage of the
CastTo<T>
class is when it allows casts that are possible non-boxed, like(int)double
in a generic context. As such(int)(object)double
fails in these scenarios.I have used
Expression.ConvertChecked
instead ofExpression.Convert
so that arithmetic overflows and underflows are checked (ie results in exception). Since il is generated during run time, and checked settings are a compile time thing, there is no way you can know the checked context of calling code. This is something you have to decide yourself. Choose one, or provide overload for both (better).If a cast doesn't exist from
TSource
toTTarget
, exception is thrown while the delegate is compiled. If you want a different behaviour, like get a default value ofTTarget
, you can check type compatibility using reflection before compiling delegate. You have the full control of the code being generated. Its going to be extremely tricky though, you have to check for reference compatibility (IsSubClassOf
,IsAssignableFrom
), conversion operator existence (going to be hacky), and even for some built in type convertibility between primitive types. Going to be extremely hacky. Easier is to catch exception and return default value delegate based onConstantExpression
. Just stating a possibility that you can mimic behaviour ofas
keyword which doesnt throw. Its better to stay away from it and stick to convention.Here's a very straight forward solution with C# 7.3's unmanaged generic type constraint:
Requires unsafe toggle in your project configuration.
Usage:
I know I'm way late to the party, but if you just need to do a safe cast like this you can use the following using
Delegate.CreateDelegate
:now without writing
Reflection.Emit
or expression trees you have a method that will convert int to enum without boxing or unboxing. Note thatTEnum
here must have an underlying type ofint
or this will throw an exception saying it cannot be bound.Edit: Another method that works too and might be a little less to write...
This works to convert your 32bit or less enum from a TEnum to an int. Not the other way around. In .Net 3.5+, the
EnumEqualityComparer
is optimized to basically turn this into a return(int)value
;You are paying the overhead of using a delegate, but it certainly will be better than boxing.
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:
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!
...I'm even 'later' : )
but just to extend on the previous post (Michael B), which did all the interesting work
and got me interested into making a wrapper for a generic case (if you want to cast generic to enum actually)
...and optimized a bit... (note: the main point is to use 'as' on Func<>/delegates instead - as Enum, value types do not allow it)
...and you can use it like this...
...for (int) - just (int)rel