Способ анализа строки перечисления .NET или значения int, связанного с флагами. - PullRequest
15 голосов
/ 26 апреля 2011

Есть хороший способ выяснить элемент перечисления, используя следующий подход:

// memberType is enum type
if (Enum.IsDefined(memberType, valueString))
{
    return Enum.Parse(memberType, valueString);
}
else
{
    try 
    {
        var underlyingValue = Convert.ChangeType(valueString, Enum.GetUnderlyingType(memberType));
        if (Enum.IsDefined(memberType, underlyingValue))
        {
            return underlyingValue;
        }
    }
    catch...
}

Это работает как шарм.За исключением значений, построенных из перечислений, отмеченных FlagsAttribute.Например, для этого перечисления и значения:

[Flags]
enum MyEnum {
    One = 0x1,
    Two = One << 1,
    Four = One << 2,
    Eight = One << 3
}

var e = MyEnum.One | MyEnum.Eight;

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

Ответ:

Последний метод выглядит следующим образом:

var parsed = Enum.Parse(memberType, valueString);
decimal d;
if (!decimal.TryParse(parsed.ToString(), out d))
{
    return parsed;
}
throw new ArgumentOutOfRangeException(memberInfo.Name, valueString, "Bad configuration parameter value.");

Ответы [ 5 ]

4 голосов
/ 27 апреля 2011

Наверное, лучше задать вопрос, как выявлять неверные значения.

Похоже, что в C # 4.0 в Nutshell есть хорошая работа.С здесь .После того, как вы проанализируете целочисленное значение в enum, вы можете использовать это и посмотреть, является ли значение допустимым.Это будет работать для комбинированных флагов.

static bool IsFlagDefined(Enum e)
{
    decimal d;
    return !decimal.TryParse(e.ToString(), out d);
}
3 голосов
/ 26 апреля 2011

Это ожидаемое поведение, так как вы можете получить значения, которые не соответствуют флагам.Например, допустим, значение a = 1, b = 2, c = 4, d = 8 и т. Д. (Просто стандартная двоичная прогрессия).Можно иметь 5 для значения (a & c) или 7 (a, b & c).

1 голос
/ 27 апреля 2011

Используйте Enum.Parse. Затем проверьте по возвращаемому значению, что не установлены биты, которые не являются допустимыми флагами. Для этого создайте битовую маску, содержащую все допустимые значения, и ИЛИ их вместе со значением. Если результат отличается от маски, было установлено несколько битов, которые не являются допустимыми флагами. Это не слишком утомительно (но вам нужно проверить, действительно ли Enum является перечислением Flags, прежде чем применять эту логику - если это не перечисление Flags, используйте IsDefined).

Код может выглядеть примерно так; и вы можете хранить маску для каждого предварительно вычисленного типа, если считаете, что каждый раз вычислять ее может быть слишком дорого (вероятно, это не так, но это будет зависеть от вашего случая):

object value = Enum.Parse(memberType, valueString);
int numericValue = (int)value;
int definedMask = Enum.GetValues(memberType).Cast<int>().Aggregate(0, (v,a) => v | a);
if ((definedMask | numericValue) != definedMask)
    throw new InvalidOperationException(String.Format("{0} is not a valid {1} value.", valueString, memberType.Name));
return value;
1 голос
/ 26 апреля 2011

Почему бы вам просто не использовать Enum.Parse ()

var test = MyEnum.One | MyEnum.Eight;
var str = test.ToString();
var back = (MyEnum) enum.Parse(typeof(MyEnum), str); // this returns MyEnum.One | MyEnum.Eight

Сначала попытайтесь преобразовать вашу строку в "неопределяемый тип", если получится, - приведите ее к MyEnum, и это то, что вам нужно.Если конвертация не удалась - попробуйте использовать Enum.Parse.Если также происходит сбой - это очень плохой ввод (исключение выброса)


Обновление: Не требуется тестирование для преобразования int enum.Parse(typeof(MyEnum), "9") возвращает MyEnum.One | MyEnum.Eight (протестировано в FrameworkОт 2.0 до 4.0)


Обновление 2: Таким образом, вопрос на самом деле был "Как определить плохие значения?".Грязное решение:

var res = Enum.Parse(targetType, str);
bool isBad;
try
{
    Convert(res.ToString(), underType);
    isBad = true;
}
catch
{
    // convert failed
    isBad = false;
}

if (isBad)
    throw new Exeption();
return res;

Но это очень грязно, и исключение является дорогостоящей операцией, поэтому здесь снижается производительность ... Но это работает)

0 голосов
/ 27 апреля 2011

Это хороший вопрос, ваше предложение

попытаться получить все значения перечисления и побитовое И их с входным значением

после 10 минут исследования выглядит действительно разумно .. ^^

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

Разбивка флагов C # на отдельные значения для сравнения

...