Как установить флаг перечисления для универсального типа перечисления в .NET Standard? - PullRequest
2 голосов
/ 04 апреля 2019

Я хочу реализовать следующий метод с использованием .NET Standard:

public static void SetFlag<TEnum>(ref TEnum value, TEnum flag)
    where TEnum : Enum

Я потратил часы, пытаясь добиться этого:

  • Получение оператора | через отражение представляется невозможным для примитивных типов, так как enum s.
  • Использование dynamic требует ссылки на дополнительный пакет (Microsoft.CSharp.RuntimeBinder), но я быкак моя библиотека, чтобы поддерживать чистоту .NET Standard.

Моей последней идеей было вручную сравнить TEnum с каждым допустимым типом перечисления {byte, sbyte, short, ushort, int, uint, long, ulong} .Но это кажется странным и грязным:

try
{
    var v = (byte)(object)value | (byte)(object)flag;
    value = (TEnum)(object)v;
    return;
}
catch (InvalidCastException) { }

try
{
    var v = (int)(object)value | (int)(object)flag;
    value = (TEnum)(object)v;
    return;
}
catch (InvalidCastException) { }

// ...

throw new NotSupportException($"Unknown enum type {typeof(TEnum)}");

Так это действительно единственный вариант .NET (Standard), предлагаемый здесь, или что мне не хватает?Ждем ваших подсказок!

Редактировать: Не дубликат этот вопрос ;Я использую C # 7.3 и общее ограничение Enum.

1 Ответ

3 голосов
/ 04 апреля 2019

Это не самое дешевое (все упаковано, есть отражения и т. Д.), Но вы всегда можете сделать что-то вроде этого:

private static void SetFlag<T>(ref T value, T flag) where T : Enum
{
    // 'long' can hold all possible values, except those which 'ulong' can hold.
    if (Enum.GetUnderlyingType(typeof(T)) == typeof(ulong))
    {
        ulong numericValue = Convert.ToUInt64(value);
        numericValue |= Convert.ToUInt64(flag);
        value = (T)Enum.ToObject(typeof(T), numericValue);
    }
    else
    {
        long numericValue = Convert.ToInt64(value);
        numericValue |= Convert.ToInt64(flag);
        value = (T)Enum.ToObject(typeof(T), numericValue);
    }
}

У вас все еще есть некоторое повторение, но, по крайней мере, оно ограничено long/ulong. Если вы можете предположить, что члены перечисления ваших флагов не будут иметь отрицательных значений, вы можете просто использовать:

private static void SetFlag<T>(ref T value, T flag) where T : Enum
{
    ulong numericValue = Convert.ToUInt64(value);
    numericValue |= Convert.ToUInt64(flag);
    value = (T)Enum.ToObject(typeof(T), numericValue);
}
...