Почему следующие два метода генерируют один и тот же IL? - PullRequest
3 голосов
/ 20 июня 2011
 public static class Extensions
{
    public static T Include<T>(this System.Enum type,T value) where T:struct 
    {
      return ((T) (ValueType) (((int) (ValueType) type | (int) (ValueType) value)));

    }
    public static T Include1<T>(this System.Enum type, T value) 
    {
        return ((T)(object)((int)(object)type | (int)(object)value));

    }
}

Если вы видите IL, сгенерированный для этих двух методов, они выглядят одинаково или я что-то упустил ... почему бокс происходит для первого метода Include?

1 Ответ

4 голосов
/ 20 июня 2011

ValueType является ссылочным типом. Честный. Это только структура, когда она T. Вам нужно будет заменить все ValueType на T, чтобы он не упаковывался. Тем не менее, не будет никакого встроенного приведения от T до int ... так что вы не можете. Вам придется боксировать. Кроме того, не все перечисления основаны на int (например, ваше поле-как-enum, unbox-as-int завершится ошибкой для enum Foo : ushort).

В C # 4.0 dynamic может быть нахальным способом сделать это:

public static T Include<T>(this T type, T value) where T : struct
{
    return ((dynamic)type) | value;
}

В противном случае, некоторое метапрограммирование (в основном для того, чтобы делать то, что делает dynamic, но вручную):

static void Main()
{
    var both = Test.A.Include(Test.B);
}
enum Test : ulong
{
    A = 1, B = 2
}

public static T Include<T>(this T type, T value) where T : struct
{
    return DynamicCache<T>.or(type, value);
}
static class DynamicCache<T>
{
    public static readonly Func<T, T, T> or;
    static DynamicCache()
    {
        if(!typeof(T).IsEnum) throw new InvalidOperationException(typeof(T).Name + " is not an enum");
        var dm = new DynamicMethod(typeof(T).Name + "_or", typeof(T), new Type[] { typeof(T), typeof(T) }, typeof(T),true);
        var il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Or);
        il.Emit(OpCodes.Ret);
        or = (Func<T, T, T>)dm.CreateDelegate(typeof(Func<T, T, T>));
    }
}
...