Кто-либо знает хорошее обходное решение из-за отсутствия перечислимого универсального ограничения?

То, что я хочу сделать, является чем-то вроде этого: у Меня есть перечисления с объединенными отмеченными значениями.

public static class EnumExtension
{
    public static bool IsSet<T>( this T input, T matchTo ) 
        where T:enum //the constraint I want that doesn't exist in C#3
    {    
        return (input & matchTo) != 0;
    }
}

Таким образом я мог сделать:

MyEnum tester = MyEnum.FlagA | MyEnum.FlagB

if( tester.IsSet( MyEnum.FlagA ) )
    //act on flag a

К сожалению, дженерик C#, где ограничения не имеют никакого перечислимого ограничения, только класс и структура. C# не рассматривает перечисления как структуры (даже при том, что они - типы значения), таким образом, я не могу добавить дополнительные типы как это.

Кто-либо знает обходное решение?

87
задан Ijas Ameenudeen 20 January 2019 в 13:55
поделиться

7 ответов

РЕДАКТИРОВАТЬ: Теперь он доступен в версии 0.0.0.2 UnconstrainedMelody.

(В соответствии с просьбой в моем сообщении в блоге об ограничениях перечисления . приведенные ниже факты для отдельного ответа.)

Лучшее решение - дождаться, когда я включу его в UnconstrainedMelody 1 . Это библиотека, которая берет код C # с «фальшивыми» ограничениями, такими как

where T : struct, IEnumConstraint

, и превращает его в

where T : struct, System.Enum

на этапе пост-сборки.

Не должно быть слишком сложно написать IsSet . .. хотя поддержка как Int64 , так и UInt64 флагов может быть сложной задачей. (Я чувствую появление некоторых вспомогательных методов, которые позволяют мне обрабатывать любые перечисления флагов, как если бы они имели базовый тип UInt64 . )

Каким будет поведение, если вы позвоните

tester.IsSet(MyFlags.A | MyFlags.C)

? Должен ли он проверить, что установлены все указанные флаги? Это было бы моим ожиданием.

Я постараюсь сделать это по дороге домой сегодня вечером ... Я надеюсь быстро найти полезные методы перечисления, чтобы быстро довести библиотеку до пригодного для использования стандарта, а затем расслабьтесь немного.

РЕДАКТИРОВАТЬ: Кстати, я не уверен насчет имени IsSet . Параметры:

  • Включает
  • Содержит
  • HasFlag (или HasFlags)
  • IsSet (это, безусловно, вариант)

Мысли приветствуются. Я уверен, что пройдет некоторое время, прежде чем что-нибудь будет высечено в камне ...


1 или, конечно, отправьте это как патч ...

Я попробую сделать это по дороге домой сегодня вечером ... Я надеюсь быстро познакомиться с полезными методами перечисления, чтобы быстро довести библиотеку до удобного стандарта, а затем немного расслабиться.

РЕДАКТИРОВАТЬ: Я кстати, не уверен насчет имени IsSet . Параметры:

  • Включает
  • Содержит
  • HasFlag (или HasFlags)
  • IsSet (это, безусловно, вариант)

Мысли приветствуются. Я уверен, что пройдет некоторое время, прежде чем что-нибудь будет высечено в камне ...


1 или, конечно, отправьте это как патч ...

Я попробую сделать это по дороге домой сегодня вечером ... Я надеюсь быстро познакомиться с полезными методами перечисления, чтобы быстро довести библиотеку до удобного стандарта, а затем немного расслабиться.

РЕДАКТИРОВАТЬ: Я кстати, не уверен насчет имени IsSet . Параметры:

  • Включает
  • Содержит
  • HasFlag (или HasFlags)
  • IsSet (это, безусловно, вариант)

Мысли приветствуются. Я уверен, что пройдет некоторое время, прежде чем что-нибудь будет высечено в камне ...


1 или, конечно, отправьте это как патч ...

s конечно вариант)

Мысли приветствуются. Я уверен, что пройдет некоторое время, прежде чем что-нибудь будет высечено в камне ...


1 или, конечно, отправьте это как патч ...

s конечно вариант)

Мысли приветствуются. Я уверен, что пройдет некоторое время, прежде чем что-нибудь будет высечено в камне ...


1 или, конечно, отправьте это как патч ...

48
ответ дан 24 November 2019 в 07:51
поделиться

Darren, который работал бы, если бы типы были определенными перечислениями - для общих перечислений для работы, необходимо бросить их к ints (или более вероятно uint), чтобы сделать булеву математику:

public static bool IsSet( this Enum input, Enum matchTo )
{
    return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}
16
ответ дан Ronnie 24 November 2019 в 07:51
поделиться

Используя Ваш исходный код, в методе можно также использовать отражение для тестирования этого, T является перечислением:

public static class EnumExtension
{
    public static bool IsSet<T>( this T input, T matchTo )
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException("Must be an enum", "input");
        }
        return (input & matchTo) != 0;
    }
}
1
ответ дан Keith 24 November 2019 в 07:51
поделиться

Как я это делаю, я устанавливаю ограничение структуры, а затем проверяю, является ли T перечислением во время выполнения. Это не устраняет проблему полностью, но несколько снижает ее

3
ответ дан 24 November 2019 в 07:51
поделиться

Вот код, который я только что сделал это, похоже, работает так, как вы хотите, без необходимости делать что-то слишком сумасшедшее. Это не ограничивается только перечислениями, установленными как флаги, но всегда можно поставить проверку, если потребуется.

public static class EnumExtensions
{
    public static bool ContainsFlag(this Enum source, Enum flag)
    {
        var sourceValue = ToUInt64(source);
        var flagValue = ToUInt64(flag);

        return (sourceValue & flagValue) == flagValue;
    }

    public static bool ContainsAnyFlag(this Enum source, params Enum[] flags)
    {
        var sourceValue = ToUInt64(source);

        foreach (var flag in flags)
        {
            var flagValue = ToUInt64(flag);

            if ((sourceValue & flagValue) == flagValue)
            {
                return true;
            }
        }

        return false;
    }

    // found in the Enum class as an internal method
    private static ulong ToUInt64(object value)
    {
        switch (Convert.GetTypeCode(value))
        {
            case TypeCode.SByte:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
                return (ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture);

            case TypeCode.Byte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
                return Convert.ToUInt64(value, CultureInfo.InvariantCulture);
        }

        throw new InvalidOperationException("Unknown enum type.");
    }
}
1
ответ дан 24 November 2019 в 07:51
поделиться

На самом деле, это возможно, с уродливой уловкой. Однако его нельзя использовать для методов расширения.

public abstract class Enums<Temp> where Temp : class {
    public static TEnum Parse<TEnum>(string name) where TEnum : struct, Temp {
        return (TEnum)Enum.Parse(typeof(TEnum), name); 
    }
}
public abstract class Enums : Enums<Enum> { }

Enums.IsSet<DateTimeKind>("Local")

При желании вы можете предоставить Enums частный конструктор и открытый вложенный абстрактный унаследованный класс с Temp в качестве Enum , чтобы предотвратить унаследованные версии для не перечислений.

9
ответ дан 24 November 2019 в 07:51
поделиться

Это не отвечает на исходный вопрос, но теперь в .NET 4 есть метод под названием Enum.HasFlag , который делает то, что вы пытаетесь сделать в своем примере.

4
ответ дан 24 November 2019 в 07:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: