C# non-boxing conversion of generic enum to int?
Asked Answered
C

9

77

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.

Chipmunk answered 27/7, 2009 at 16:18 Comment(11)
Have you viewed the IL for your example? Does it do any boxing?Tacitus
I edited your question to prove that your guess was indeed correct -- boxing does occur.Tacitus
I confirmed the boxing/unboxing via Reflector. I also edited the question before seeing that you did so. My apologies for overwriting your edit.Chipmunk
No problem. Sorry I can't give you an answer but after looking at this question for a few minutes, I'm not sure what you want is possible in C#.Tacitus
I'm stuck on this one. Can we step back a little and ask why you need this? What does the code that calls this method look like?Tacitus
It is not required for correctness. I was simply following through on a friend's suggestion to avoid a boxing conversion.Chipmunk
Unfortunately the jitter does NOT turn verfiably safe box-followed-immediately-by-unbox into a no-op. We have several situations where the C# compiler is forced to generate such code to make the verifier happy; this is just one of them. The jitter team is aware of the issue and I hope that future versions of the jitter do a good job of optimizing this pattern.Oxus
@Jeffrey - Understood, but if the caller knows that the type is an enum, it can just cast directly itself. I'm wondering what scenario disallows that.Tacitus
@Drew - The cast is framework internal implementation and should not be exposed to caller. I do appreciate the thought in that direction, but the "why" is outside the scope of this question.Chipmunk
@EricLippert Is that still the case 6 years on?Raynell
@Basic: I have no idea. Ask someone on the jitter team.Oxus
I
63

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:

static int Generic<T>(T t)
{
    int variable = -1;

    // may be a type check - if(...
    variable = CastTo<int>.From(t);

    return variable;
}

The class:

/// <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 the caster func with other implementations. I will compare performance of a few:

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>));
}

Boxed casts:

  1. int to int

    object casting -> 42 ms
    caster1 -> 102 ms
    caster2 -> 102 ms
    caster3 -> 90 ms
    caster4 -> 101 ms

  2. int to int?

    object casting -> 651 ms
    caster1 -> fail
    caster2 -> fail
    caster3 -> 109 ms
    caster4 -> fail

  3. int? to int

    object casting -> 1957 ms
    caster1 -> fail
    caster2 -> fail
    caster3 -> 124 ms
    caster4 -> fail

  4. enum to int

    object casting -> 405 ms
    caster1 -> fail
    caster2 -> 102 ms
    caster3 -> 78 ms
    caster4 -> fail

  5. int to enum

    object casting -> 370 ms
    caster1 -> fail
    caster2 -> 93 ms
    caster3 -> 87 ms
    caster4 -> fail

  6. int? to enum

    object casting -> 2340 ms
    caster1 -> fail
    caster2 -> fail
    caster3 -> 258 ms
    caster4 -> fail

  7. enum? to int

    object casting -> 2776 ms
    caster1 -> fail
    caster2 -> fail
    caster3 -> 131 ms
    caster4 -> fail


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:

  1. int to double

    object casting -> fail
    caster1 -> fail
    caster2 -> fail
    caster3 -> 109 ms
    caster4 -> 118 ms

  2. enum to int?

    object casting -> fail
    caster1 -> fail
    caster2 -> fail
    caster3 -> 93 ms
    caster4 -> fail

  3. int to enum?

    object casting -> fail
    caster1 -> fail
    caster2 -> fail
    caster3 -> 93 ms
    caster4 -> fail

  4. enum? to int?

    object casting -> fail
    caster1 -> fail
    caster2 -> fail
    caster3 -> 121 ms
    caster4 -> fail

  5. int? to enum?

    object casting -> fail
    caster1 -> fail
    caster2 -> fail
    caster3 -> 120 ms
    caster4 -> fail

For the fun of it, I tested a few reference type conversions:

  1. PrintStringProperty to string (representation changing)

    object casting -> fail (quite obvious, since it is not cast back to original type)
    caster1 -> fail
    caster2 -> fail
    caster3 -> 315 ms
    caster4 -> fail

  2. string to object (representation preserving reference conversion)

    object casting -> 78 ms
    caster1 -> fail
    caster2 -> fail
    caster3 -> 322 ms
    caster4 -> fail

Tested like this:

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:

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

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

  3. I have used Expression.ConvertChecked instead of Expression.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).

  4. If a cast doesn't exist from TSource to TTarget, exception is thrown while the delegate is compiled. If you want a different behaviour, like get a default value of TTarget, 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 on ConstantExpression. Just stating a possibility that you can mimic behaviour of as keyword which doesnt throw. Its better to stay away from it and stick to convention.

Indigoid answered 30/4, 2014 at 15:9 Comment(2)
+1, I like this approach. The CreateDelegate seems like a hack to me. In fact, in mono, the CreateDelegate approach failed, this one kept working.Fulbright
This example is also documented in the .NET doc msdn.microsoft.com/en-us/library/bb292051(v=vs.110).aspxChansoo
E
38

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:

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 throw an exception saying it cannot be bound.

Edit: Another method that works too and might be a little less to write...

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

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.

This was fairly old, but if you're still coming back here looking for a solution that works on .net 5/.Net core (or netfx with the unsafe package) and remains optimal...

[JitGeneric(typeof(StringComparison), typeof(int))]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryConvert<TEnum, T>(this TEnum @enum, out T val)
    where TEnum : struct, Enum
    where T : struct, IConvertible, IFormattable, IComparable
{
    if (Unsafe.SizeOf<T>() == Unsafe.SizeOf<TEnum>())
    {
        val = Unsafe.As<TEnum, T>(ref @enum);
        return true;
    }
    val = default;
    return false;
}

An example usage might be like so::

public static int M(MethodImplOptions flags) => flags.TryConvert(out int v) ? v : 0;

Here we can see on sharplab, that this method gets inlined entirely away:: https://sharplab.io/#gist:802b8d21ee1de26e791294ba48f69d97

Ergocalciferol answered 26/10, 2010 at 18:10 Comment(2)
This situation came around again, in my client's codebase. This time, I ended up using this solution. Thanks!Chipmunk
You should probably remove the JitGenericAttribute as this is for SharpLab only and can't be used elsewhere.Disparage
T
19

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!

Tacitus answered 27/7, 2009 at 16:36 Comment(2)
Many thanks. I'll look at doing this eventually. For now I will leave the boxing conversion in place.Chipmunk
A function that takes type Int32 can be bound to a delegate that takes an enumerated type that resolves to Int32 (and likewise for other numeric types). If one had a bunch of overloaded functions to convert a Byte, SByte, Int16, UInt16, etc. into an Int64, one could use reflection to bind a Func<T,Int64> to one of those functions the first time an attempt was made to convert T to an Int64, and then use the cached delegate every time thereafter. Much faster than boxing (a HasAny<T> extension method is about 30x as fast as the Enum.HasFlag(Enum) extension method)Letishaletitia
J
4

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

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

Jackquelinejackrabbit answered 2/3, 2012 at 19:35 Comment(10)
This doesn't seem to work. Func<TEnum, TEnum> cannot be cast to either a Func<T, TEnum> or a Func<Enum, T>. The result of the as operator is null. If you use a traditional cast instead, the compiler will then complain.Chipmunk
Jeff, yes, that's not allowed as I mentioned (please read the 'note' abovve) - you need to use an 'identity', cast a delegate instead - exactly as I did it (FamilyRelation rel = Identity<FamilyRelation, P>.Cast(value);). That example works 'as is' (compiled and tested and I'm using it in couple projects, haven't tried new .NET/C#/VS yet). Note this isn't the exact answer to you Q but an 'elaboration'. It's there to be useful to somebody hopefully. And I'm using boxing but unrelated, not for enum. You may need to plug-in, work out the specifics.Jackquelinejackrabbit
...as you're already casting delegates - you're probably trying to plug in the 'int' and enum together - that won't work. This is to bring down your 'open' enum type to a real/closed enum - which is more useful in most cases. Then you can cast (int)Identity<FamilyRelation, P>.Cast(value). However it won't help if you have an 'unknown' enum type (i.e. you want to mix and serialize many different enums to one 'int' e.g. db field), though that's rarely the case (design considerations) but plausible in certain cases.Jackquelinejackrabbit
A minor suggestion, wouldnt declaring it Identity<T, TEnum> make more sense?Indigoid
I think you're right @Indigoid :) - I remember seeing that at the time - but it was just a fast re-write of the above code, so it stuck. The original point (I had) was to convey the concept which turned out interesting (to me at least), but I never meant it as a proper code. I think my local version is a bit upgraded since then. Will revisit when I manage the time, thx.Jackquelinejackrabbit
@Jackquelinejackrabbit hmmm, I like your approach, as it strictly does only identity casts, like int to int, hence the name Identity. Even though boxed conversion exists between int to int?, or enum to int?, your approach doesn't incorporate that, which in a way I like it. Mentioning, just so that someone reading this will realize that. Its easy to write a more correct cast approach using Expression.Convert, I will definitely make it an answer, when I get time.Indigoid
Hum, you could use the presence of the Cast to test castability: if(Identity<T,P>.Cast!=null) return Identity<T,P>.Cast(value);Gherkin
@Rbjz I think you mean if (Identity<FamilyRelation, P>.Cast != null) - and you're absolutely right. That Cast is null so a good check.Jackquelinejackrabbit
@Jackquelinejackrabbit I mean the null check could be used instead of reflection. See my application of the pattern here: gist.github.com/jocutajar/5e41e48b4a665baa78ea55293415f41a - I'm not using it with enums, it solves another problem there. The generic type parameter casting issue. You'd have to treat structs and refs differently otherwise or resort to boxing.Gherkin
@Rbjz I think we agree - and feel free to edit the post if you have tested it and works, I have very little time and I'm not very active lately. This was quite simplified code at the time just to show the idea (and that if is ugly), that static cast method.Jackquelinejackrabbit
R
3

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.

Ropedancer answered 27/7, 2009 at 16:33 Comment(1)
Snap. BTW you can create verifiable IL using Reflection.Emit for this scenario.Tacitus
S
3

Here is a simplest and fastest way.
(with a little 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;
    }
}

Restriction:
This works in Mono. (ex. Unity3D)

More information about Unity3D:
ErikE's CastTo class is a really neat way to solve this problem.
BUT it can't be used as is in Unity3D

First, it have to be fixed like below.
(because that the mono compiler can't 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);
    }
}

Second, ErikE's code can't be used in AOT platform.
So, my code is the best solution for Mono.

To commenter 'Kristof':
I am sorry that I didn't write all the details.

Snuck answered 27/7, 2016 at 7:10 Comment(4)
Interesting approach, but whether it's really the fastest I don't know. I have seen detrimental performance curves when using LayoutKind.Explicit to force this kind of hacks. I'd be interested in you showing some timings compared to the alternatives in this thread.Oran
Doesn't work. An unhandled exception of type 'System.TypeLoadException' occurred in x Additional information: Could not load type 'EnumUnion32`1' from assembly x, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because generic types cannot have explicit layout.Phenolphthalein
This does not work in Mono, as Kristof wrote in bold. So what exactly does "I am sorry that I didn't write all the details" mean in reply to that?Volotta
I just tried this is Unity and similarly was unable to get it working.Tamasha
W
3

Here's a very straight forward solution with C# 7.3's unmanaged generic type constraint:

    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>'s 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>'s 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 toggle in your project configuration.

Usage:

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

Edit: Replaced Buffer.MemoryCopy by simple pointer cast from dahall's suggestion.

Wallinga answered 21/10, 2018 at 10:58 Comment(3)
I like this approach if you already have or can have unsafe projects. In that case, couldn't you also just use pointers to do the work of MemoryCopy? return *(TEnum*)(void*)&value;Unstep
@Unstep Totally, I wasn't very familiar with pointers at the time, post edited.Wallinga
I think you can just use Unsafe.As methodCarlettacarley
R
0

If you'd like to speed up conversion, restricted to use unsafe code and can't emit IL you may want to consider to make generic class as abstract and implement conversion in derived classes. For example, when you code for Unity engine you probably want to build IL2CPP targets which are not compatible with emit. Here is an example of how it can be implemented:

// Generic scene resolver is abstract and requires
// to implement enum to index conversion
public abstract class SceneResolver<TSceneTypeEnum> : ScriptableObject
    where TSceneTypeEnum : Enum
{
    protected ScenePicker[] Scenes;

    public string GetScenePath ( TSceneTypeEnum sceneType )
    {
        return Scenes[SceneTypeToIndex( sceneType )].Path;
    }

    protected abstract int SceneTypeToIndex ( TSceneTypeEnum sceneType );
}

// Here is some enum for non-generic class
public enum SceneType
{
}

// Some non-generic implementation
public class SceneResolver : SceneResolver<SceneType>
{
    protected override int SceneTypeToIndex ( SceneType sceneType )
    {
        return ( int )sceneType;
    }
}

I tested boxing vs. virtual method and got 10x speed up for virtual method approach on macOS for both Mono and IL2CPP targets.

Refrigerator answered 16/12, 2019 at 8:17 Comment(0)
F
-1

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.

Fenton answered 19/5, 2015 at 7:25 Comment(2)
Serialization is an issue with this approach.Stroller
Not being able to do bit masking on the output is another.Stalnaker

© 2022 - 2024 — McMap. All rights reserved.