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

2019-01-16 03:40发布

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.

8条回答
贼婆χ
2楼-- · 2019-01-16 04:10

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.

查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-01-16 04:13

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.

查看更多
登录 后发表回答