C # non-boxing conversion of general enum to int?

Given the general TEnum parameter, which will always be an enumeration type, is there a way to pass from TEnum to int without boxing / unboxing?

See this sample code. This will result in a box / unbox 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 the release mode compiled for the following ILs (boxing note and codes for deletion):

 .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 } 

The Enum transformation was handled extensively on SO, but I could not find a discussion addressing this particular case.

+55
enums c # boxing
Jul 27 '09 at 16:18
source share
8 answers

I'm not sure if this is possible in C # without using Reflection.Emit. If you use Reflection.Emit, you can load the value of the enumeration onto the stack and then process it as if it were an int.

You have to write quite a bit of code, though, so you want to check if you really get any performance at the same time.

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 will result in an error if your enum is derived from long (integer 64 bits).

EDIT

Another thought about this approach. Reflection.Emit can create the method above, but the only way to bind to it is through a virtual call (i.e., it implements a well-known interface / abstract compilation text that you could name) or an indirect call (i.e. via a delegate call) . I assume that both of these scenarios will be slower than the overhead of boxing / unboxing anyway.

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

As with all performance-related issues: measurement, measurement, measurement!

+17
Jul 27 '09 at 16:36
source share
β€” -

This is similar to the answers posted here, but uses expression trees in order to emit il to carry between types. Expression.Convert does the trick. The compiled delegate (caster) is cached by the internal static class. Since the original object can be inferred from the argument, I assume that it offers a cleaner call. E.g. general context:

 static int Generic<T>(T t) { int variable = -1; // may be a type check - if(... variable = CastTo<int>.From(t); return variable; } 

Grade:

 /// <summary> /// Class to cast to type <see cref="T"/> /// </summary> /// <typeparam name="T">Target type</typeparam> public static class CastTo<T> { /// <summary> /// Casts <see cref="S"/> to <see cref="T"/>. /// This does not cause boxing for value types. /// Useful in generic methods. /// </summary> /// <typeparam name="S">Source type to cast from. Usually a generic type.</typeparam> public static T From<S>(S s) { return Cache<S>.caster(s); } private static class Cache<S> { public static readonly Func<S, T> caster = Get(); private static Func<S, T> Get() { var p = Expression.Parameter(typeof(S)); var c = Expression.ConvertChecked(p, typeof(T)); return Expression.Lambda<Func<S, T>>(c, p).Compile(); } } } 



You can replace caster functionality caster other implementations. I will compare the performance of several:

 direct object casting, ie, (T)(object)S caster1 = (Func<T, T>)(x => x) as Func<S, T>; caster2 = Delegate.CreateDelegate(typeof(Func<S, T>), ((Func<T, T>)(x => x)).Method) as Func<S, T>; caster3 = my implementation above caster4 = EmitConverter(); static Func<S, T> EmitConverter() { var method = new DynamicMethod(string.Empty, typeof(T), new[] { typeof(S) }); var il = method.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); if (typeof(S) != typeof(T)) { il.Emit(OpCodes.Conv_R8); } il.Emit(OpCodes.Ret); return (Func<S, T>)method.CreateDelegate(typeof(Func<S, T>)); } 

Box Insert :

  • int to int

    casting objects β†’ 42 ms
    caster1 β†’ 102 ms
    caster2 β†’ 102 ms
    caster3 β†’ 90 ms
    caster4 β†’ 101 ms

  • int to int?

    casting objects β†’ 651 ms
    caster1 β†’ fail
    caster2 β†’ fail
    caster3 β†’ 109 ms
    caster4 β†’ fail

  • int? to int

    casting objects β†’ 1957 ms
    caster1 β†’ fail
    caster2 β†’ fail
    caster3 β†’ 124 ms
    caster4 β†’ fail

  • enum to int

    casting objects β†’ 405 ms
    caster1 β†’ fail
    caster2 β†’ 102 ms
    caster3 β†’ 78 ms
    caster4 β†’ fail

  • int to enum

    casting objects β†’ 370 ms
    caster1 β†’ fail
    caster2 β†’ 93 ms
    caster3 β†’ 87 ms
    caster4 β†’ fail

  • int? to enum

    casting objects β†’ 2340 ms
    caster1 β†’ fail
    caster2 β†’ fail
    caster3 β†’ 258 ms
    caster4 β†’ fail

  • enum? to int

    casting objects β†’ 2776 ms
    caster1 β†’ fail
    caster2 β†’ fail
    caster3 β†’ 131 ms
    caster4 β†’ fail




Expression.Convert puts a direct conversion from the source type to the target type, so it can produce explicit and implicit casts (not to mention references). Thus, this makes it possible to handle casting, which is otherwise possible only when it is not boxed (i.e., in the general method, if you execute (TTarget)(object)(TSource) , it will explode if it is not identity conversion (as in the previous section) or link translation (as shown in the next section)). Therefore, I will include them in the tests.

Unblocked roles:

  • int to double

    casting objects β†’ fail
    caster1 β†’ fail
    caster2 β†’ fail
    caster3 β†’ 109 ms
    caster4 β†’ 118 ms

  • enum to int?

    casting objects β†’ fail
    caster1 β†’ fail
    caster2 β†’ fail
    caster3 β†’ 93 ms
    caster4 β†’ fail

  • int to enum?

    casting objects β†’ fail
    caster1 β†’ fail
    caster2 β†’ fail
    caster3 β†’ 93 ms
    caster4 β†’ fail

  • enum? to int?

    casting objects β†’ fail
    caster1 β†’ fail
    caster2 β†’ fail
    caster3 β†’ 121 ms
    caster4 β†’ fail

  • int? to enum?

    casting objects β†’ fail
    caster1 β†’ fail
    caster2 β†’ fail
    caster3 β†’ 120 ms
    caster4 β†’ fail

For fun, I tested several reference conversion types:

  • PrintStringProperty to string (view change)

    casting objects -> crash (quite obvious, since it is not discarded to the original type)
    caster1 β†’ fail
    caster2 β†’ fail
    caster3 β†’ 315 ms
    caster4 β†’ fail

  • string to object (saving with saving the reference conversion)

    casting β†’ 78 ms
    caster1 β†’ fail
    caster2 β†’ fail
    caster3 β†’ 322 ms
    caster4 β†’ fail

Tested as follows:

 static void TestMethod<T>(T t) { CastTo<int>.From(t); //computes delegate once and stored in a static variable int value = 0; var watch = Stopwatch.StartNew(); for (int i = 0; i < 10000000; i++) { value = (int)(object)t; // similarly value = CastTo<int>.From(t); // etc } watch.Stop(); Console.WriteLine(watch.Elapsed.TotalMilliseconds); } 



Note:

  • My assessment is that if you don't run it at least a hundred thousand times, it's not worth it and you have almost nothing to worry about boxing. Keep in mind that delegate caching has a hit in mind. But beyond this limit, speed improves, especially when it comes to casting using nullables .

  • But the real advantage of the CastTo<T> class is that it allows you to do fieldless throws, for example (int)double in the general context. As such (int)(object)double not executed in these scripts.

  • I used Expression.ConvertChecked instead of Expression.Convert so that arithmetic overflows and threads are checked (i.e. lead to an exception). Since il is generated at runtime, and the checked parameters are compilation time, you cannot find out the checked context of the call code. This is what you need to decide for yourself. Choose one of them or provide overload for both (better).

  • If casting does not exist from TSource to TTarget , an exception is thrown when the delegate is compiled. If you need other behavior, such as getting the default value of TTarget , you can check for type compatibility using reflection before compiling the delegate. You have complete control over the generated code. It will be very difficult, but you need to check the compatibility of links ( IsSubClassOf , IsAssignableFrom ), the existence of a conversion operator (will be IsAssignableFrom ), and even for some built-in type convertibility between primitive types. Be very hacks. It is easier to catch the exception and return the default delegate based on ConstantExpression . I just declare that you can simulate the behavior of the as keyword that does not throw. Better to stay away from it and stick to the convention.

+47
Apr 30 '14 at 15:09
source share

I know I'm late to the party, but if you just need to make a safe throw, 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 converts an int to an enumeration without boxing or decompression. Please note that TEnum here must have a base int type, otherwise this will throw an exception, since it cannot be bound.

Edit: Another method that works too and may be slightly less to write ...

 Func<TEnum,int> converter = EqualityComparer<TEnum>.Default.GetHashCode; 

This works to convert your 32-bit or less enumeration from TEnum to int. And not vice versa. In .Net 3.5+, EnumEqualityComparer is optimized to basically turn this into a return (int)value ;

You pay the overhead of using the delegate, but it will certainly be better than boxing.

+32
Oct 26 '10 at 18:10
source share

... I even "later" :)

but just to continue on the previous post (Michael B), who did all the interesting work

and interested us in creating a wrapper for the general case (if you want to actually sketch the general on enum)

... and a bit optimized ... (note: the main thing is to use the "how" for Func <> / delegates instead, since Enum value types do not allow this)

 public static class Identity<TEnum, T> { public static readonly Func<T, TEnum> Cast = (Func<TEnum, TEnum>)((x) => x) as Func<T, TEnum>; } 

... and you can use it like this:

 enum FamilyRelation { None, Father, Mother, Brother, Sister, }; class FamilyMember { public FamilyRelation Relation { get; set; } public FamilyMember(FamilyRelation relation) { this.Relation = relation; } } class Program { static void Main(string[] args) { FamilyMember member = Create<FamilyMember, FamilyRelation>(FamilyRelation.Sister); } static T Create<T, P>(P value) { if (typeof(T).Equals(typeof(FamilyMember)) && typeof(P).Equals(typeof(FamilyRelation))) { FamilyRelation rel = Identity<FamilyRelation, P>.Cast(value); return (T)(object)new FamilyMember(rel); } throw new NotImplementedException(); } } 

... for (int) - just (int) rel

+4
Mar 02 '12 at 19:35
source share

I think you can always use System.Reflection.Emit to create a dynamic method and emit instructions that do this without a box, although this may not be verified.

+3
Jul 27. '09 at 16:33
source share

Here is the easiest and fastest way.
(with a slight restriction. :-))

 public class BitConvert { [StructLayout(LayoutKind.Explicit)] struct EnumUnion32<T> where T : struct { [FieldOffset(0)] public T Enum; [FieldOffset(0)] public int Int; } public static int Enum32ToInt<T>(T e) where T : struct { var u = default(EnumUnion32<T>); u.Enum = e; return u.Int; } public static T IntToEnum32<T>(int value) where T : struct { var u = default(EnumUnion32<T>); u.Int = value; return u.Enum; } } 

Limitation:
It works in mono. (e.g. Unity3D)

More information about Unity3D:
The ErikE CastTo class is a really great way to solve this problem.
BUT it can not be used as in Unity3D

Firstly, this should be fixed as shown below.
(because the mono compiler cannot compile the original code)

 public class CastTo { protected static class Cache<TTo, TFrom> { public static readonly Func<TFrom, TTo> Caster = Get(); static Func<TFrom, TTo> Get() { var p = Expression.Parameter(typeof(TFrom), "from"); var c = Expression.ConvertChecked(p, typeof(TTo)); return Expression.Lambda<Func<TFrom, TTo>>(c, p).Compile(); } } } public class ValueCastTo<TTo> : ValueCastTo { public static TTo From<TFrom>(TFrom from) { return Cache<TTo, TFrom>.Caster(from); } } 

Secondly, ErikE code cannot be used on the AOT platform.
So my code is the best solution for Mono.

Commentator "Christoph":
I'm sorry that I did not write all the details.

+2
Jul 27 '16 at 7:10
source share

Here is a very simple solution with an unmanaged universal-type constraint in C # 7.3:

  using System; public static class EnumExtensions<TEnum> where TEnum : unmanaged, Enum { /// <summary> /// Converts a <typeparam name="TEnum"></typeparam> into a <typeparam name="TResult"></typeparam> /// through pointer cast. /// Does not throw if the sizes don't match, clips to smallest data-type instead. /// So if <typeparam name="TResult"></typeparam> is smaller than <typeparam name="TEnum"></typeparam> /// bits that cannot be captured within <typeparam name="TResult"></typeparam> size will be clipped. /// </summary> public static TResult To<TResult>( TEnum value ) where TResult : unmanaged { unsafe { if( sizeof(TResult) > sizeof(TEnum) ) { // We might be spilling in the stack by taking more bytes than value provides, // alloc the largest data-type and 'cast' that instead. TResult o = default; *((TEnum*) & o) = value; return o; } else { return * (TResult*) & value; } } } /// <summary> /// Converts a <typeparam name="TSource"></typeparam> into a <typeparam name="TEnum"></typeparam> /// through pointer cast. /// Does not throw if the sizes don't match, clips to smallest data-type instead. /// So if <typeparam name="TEnum"></typeparam> is smaller than <typeparam name="TSource"></typeparam> /// bits that cannot be captured within <typeparam name="TEnum"></typeparam> size will be clipped. /// </summary> public static TEnum From<TSource>( TSource value ) where TSource : unmanaged { unsafe { if( sizeof(TEnum) > sizeof(TSource) ) { // We might be spilling in the stack by taking more bytes than value provides, // alloc the largest data-type and 'cast' that instead. TEnum o = default; *((TSource*) & o) = value; return o; } else { return * (TEnum*) & value; } } } } 

Requires unsafe switching in the configuration of your project.

Using:

 int intValue = EnumExtensions<YourEnumType>.To<int>( yourEnumValue ); 

Edit: Replaced Buffer.MemoryCopy with a simple pointer from the dahall clause.

0
Oct 21 '18 at 10:58
source share

I hope I'm not too late ...

I think that you should consider solving your problem with a different approach, instead of using Enums, try creating a class with readonly public static properties.

if you use this approach, you will have an object that "feels" like Enum, but you will have all the flexibility of the class, which means that you can override any of the operators.

There are other advantages, such as a partial class, which allows you to define the same enumeration in more than one / dll file, which allows you to add values ​​to a common dll without recompiling them.

I could not find any reason not to take this approach (this class will be on the heap, not on the stack, which is slower, but worth it)

Please let me know what you think.

-one
May 19 '15 at 7:25
source share



All Articles