Перекласти Int на загальний перелік у C #


84

Подібно до Cast int до переліку в C #, але мій перелік - це параметр загального типу. Який найкращий спосіб вирішити це?

Приклад:

private T ConvertEnum<T>(int i) where T : struct, IConvertible
{
    return (T)i;
}

Генерує помилку компілятора Cannot convert type 'int' to 'T'

Повний код такий, де значення може містити int або null.

private int? TryParseInt(string value)
{
    var i = 0;
    if (!int.TryParse(value, out i))
    {
        return null;
    }
    return i;
}

private T? TryParseEnum<T>(string value) where T : struct, IConvertible
{
    var i = TryParseInt(value);
    if (!i.HasValue)
    {
        return null;
    }

    return (T)i.Value;
}


Остання відповідь на stackoverflow.com/questions/1331739/… , наближається до того, що ви хочете. Це все ще не розумно. Я схильний використовувати для цього роздуми, ви можете зробити код набагато сильнішим. Структура недостатньо стримана, щоб на мою думку вартувати возитися із дженериками.
Тоні Хопкінсон,

1
Щось, що не боксує
nawfal

Відповіді:


120

Найпростіший спосіб, який я знайшов, - це примусити руку компілятора, додавши привід object.

return (T)(object)i.Value;

12
Якщо вам не подобається бокс: c-sharp-non-boxing-conversion-of-generic-enum-to-int
nawfal

5
Ми перераховуємо enum до int, а не навпаки, як у запитанні So, яке ви посилаєте. Крім того, це питання не має рішення.
MatteoSp

Ви також можете просто виділити статичний масив зі значеннями перерахування, а потім просто передати індекс для отримання правильного переліку. Це позбавляє від необхідності робити будь-який кастинг. Приклад (До цього поняття відносяться лише рядки 11,14 та 34): pastebin.com/iPEzttM4
Krythic 02

20

Ви повинні мати можливість використовувати Enum.Parseдля цього:

return (T)Enum.Parse(typeof(T), i.Value.ToString(), true);

Ця стаття розповідає про синтаксичний аналіз загальних переліків для методів розширення:


@Guvante: Я думаю, що я перетворив значення у рядок у своєму прикладі. Ви передбачаєте, що це спричиняє проблему?
Джеймс Джонсон,

16

Ось дуже швидке рішення, яке зловживає тим, що середовище виконання створює кілька екземплярів статичних загальних класів. Розкрийте своїх демонів внутрішньої оптимізації!

Це справді світить, коли ти читаєш Enums із потоку загальним чином. Поєднуйте із зовнішнім класом, який також кешує основний тип перерахування та BitConverter, щоб розкрити чудове.

void Main() 
{
    Console.WriteLine("Cast (reference): {0}", (TestEnum)5);
    Console.WriteLine("EnumConverter: {0}", EnumConverter<TestEnum>.Convert(5));
    Console.WriteLine("Enum.ToObject: {0}", Enum.ToObject(typeof(TestEnum), 5));

    int iterations = 1000 * 1000 * 100;
    Measure(iterations, "Cast (reference)", () => { var t = (TestEnum)5; });
    Measure(iterations, "EnumConverter", () => EnumConverter<TestEnum>.Convert(5));
    Measure(iterations, "Enum.ToObject", () => Enum.ToObject(typeof(TestEnum), 5));
}

static class EnumConverter<TEnum> where TEnum : struct, IConvertible
{
    public static readonly Func<long, TEnum> Convert = GenerateConverter();

    static Func<long, TEnum> GenerateConverter()
    {
        var parameter = Expression.Parameter(typeof(long));
        var dynamicMethod = Expression.Lambda<Func<long, TEnum>>(
            Expression.Convert(parameter, typeof(TEnum)),
            parameter);
        return dynamicMethod.Compile();
    }
}

enum TestEnum 
{
    Value = 5
}

static void Measure(int repetitions, string what, Action action)
{
    action();

    var total = Stopwatch.StartNew();
    for (int i = 0; i < repetitions; i++)
    {
        action();
    }
    Console.WriteLine("{0}: {1}", what, total.Elapsed);
}

Результати щодо Core i7-3740QM з увімкненою оптимізацією:

Cast (reference): Value
EnumConverter: Value
Enum.ToObject: Value
Cast (reference): 00:00:00.3175615
EnumConverter: 00:00:00.4335949
Enum.ToObject: 00:00:14.3396366

2
Це дуже приємно, дякую. Можливо, ви хотіли б використовувати Expression.ConvertCheckedзамість цього, так що числове переповнення діапазону типу перерахування призводить до OverflowException.
Дрю Ноукс

Ваш пробіг може змінюватися, я запустив код на try.dot.net (blazor), і там EnumConverter <T> набагато повільніший за альтернативи. Кастинг на об'єкт спочатку був приблизно в 6 разів повільніший, ніж прямий кидок, але все ж набагато кращий, ніж інші варіанти.
Герман



0
public static class Extensions
    {
        public static T ToEnum<T>(this int param)
        {
            var info = typeof(T);
            if (info.IsEnum)
            {
                T result = (T)Enum.Parse(typeof(T), param.ToString(), true);
                return result;
            }

            return default(T);
        }
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.