Вопрос

У меня есть перечисление флагов ниже.

[Flags]
public enum FlagTest
{
    None = 0x0,
    Flag1 = 0x1,
    Flag2 = 0x2,
    Flag3 = 0x4
}

Я не могу заставить оператор if принять значение true.

FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2;

if (testItem == FlagTest.Flag1)
{
    // Do something,
    // however This is never true.
}

Как я могу сделать это правдой?

Это было полезно?

Решение

В .NET 4 появился новый метод Enum.HasFlag . Это позволяет вам написать:

if ( testItem.HasFlag( FlagTest.Flag1 ) )
{
    // Do Stuff
}

что намного удобнее для чтения, ИМО.

Источник .NET указывает, что он выполняет ту же логику, что и принятый ответ:

public Boolean HasFlag(Enum flag) {
    if (!this.GetType().IsEquivalentTo(flag.GetType())) {
        throw new ArgumentException(
            Environment.GetResourceString(
                "Argument_EnumTypeDoesNotMatch", 
                flag.GetType(), 
                this.GetType()));
    }

    ulong uFlag = ToUInt64(flag.GetValue()); 
    ulong uThis = ToUInt64(GetValue());
    // test predicate
    return ((uThis & uFlag) == uFlag); 
}

Другие советы

if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
     // Do something
}

(testItem & amp; FlagTest.Flag1) является побитовой операцией AND.

FlagTest.Flag1 эквивалентен 001 с перечислением OP. Теперь предположим, что testItem имеет Flag1 и Flag2 (так что это поразрядно 101 ):

  001
 &101
 ----
  001 == FlagTest.Flag1

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

if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
    // Do stuff.
}

testItem (в соответствии с вопросом) определяется как

testItem 
 = flag1 | flag2  
 = 001 | 010  
 = 011

Тогда в операторе if левая часть сравнения будет

(testItem & flag1) 
 = (011 & 001) 
 = 001

И полный оператор if (который имеет значение true, если flag1 установлен в testItem ),

(testItem & flag1) == flag1
 = (001) == 001
 = true

@фил-девейни

Обратите внимание, что, за исключением простейших случаев, Перечисление.HasFlag это приводит к значительному снижению производительности по сравнению с написанием кода вручную.Рассмотрим следующий код:

[Flags]
public enum TestFlags
{
    One = 1,
    Two = 2,
    Three = 4,
    Four = 8,
    Five = 16,
    Six = 32,
    Seven = 64,
    Eight = 128,
    Nine = 256,
    Ten = 512
}


class Program
{
    static void Main(string[] args)
    {
        TestFlags f = TestFlags.Five; /* or any other enum */
        bool result = false;

        Stopwatch s = Stopwatch.StartNew();
        for (int i = 0; i < 10000000; i++)
        {
            result |= f.HasFlag(TestFlags.Three);
        }
        s.Stop();
        Console.WriteLine(s.ElapsedMilliseconds); // *4793 ms*

        s.Restart();
        for (int i = 0; i < 10000000; i++)
        {
            result |= (f & TestFlags.Three) != 0;
        }
        s.Stop();
        Console.WriteLine(s.ElapsedMilliseconds); // *27 ms*        

        Console.ReadLine();
    }
}

На протяжении 10 миллионов итераций метод расширения HasFlags занимает колоссальные 4793 мс по сравнению с 27 мс для стандартной побитовой реализации.

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

В основном:

public static bool IsSet( this Enum input, Enum matchTo )
{
    return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}

Тогда вы можете сделать:

FlagTests testItem = FlagTests.Flag1 | FlagTests.Flag2;

if( testItem.IsSet ( FlagTests.Flag1 ) )
    //Flag1 is set

Кстати, соглашение, которое я использую для перечислений, является единственным для стандарта, множественным для флагов.Таким образом, по имени перечисления вы узнаете, может ли оно содержать несколько значений.

Еще один совет ... Никогда не выполняйте стандартную двоичную проверку с флагом, значение которого равно "0". Ваша проверка этого флага всегда будет верна.

[Flags]
public enum LevelOfDetail
{
    [EnumMember(Value = "FullInfo")]
    FullInfo=0,
    [EnumMember(Value = "BusinessData")]
    BusinessData=1
}
<Ч>

Если вы двоично проверяете входной параметр на FullInfo - вы получите:

detailLevel = LevelOfDetail.BusinessData;
bool bPRez = (detailLevel & LevelOfDetail.FullInfo) == LevelOfDetail.FullInfo;

bPRez всегда будет верным как НИЧЕГО & amp; 0 всегда == 0.

<Ч>

Вместо этого вы должны просто проверить, что значение ввода равно 0:

bool bPRez = (detailLevel == LevelOfDetail.FullInfo);
if((testItem & FlagTest.Flag1) == FlagTest.Flag1) 
{
...
}

Для битовых операций вам необходимо использовать побитовые операторы.

Это должно сработать:

if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
    // Do something,
    // however This is never true.
}

Редактировать: Исправлена моя проверка if - я вернулся к своим методам C / C ++ (спасибо Райану Фарли за указание на это)

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

т. е.

public class FlagTestCompare
{
    public static bool Compare(this FlagTest myFlag, FlagTest condition)
    {
         return ((myFlag & condition) == condition);
    }
}

Попробуйте это:


if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
    // do something
}
По сути, ваш код спрашивает, совпадают ли установленные оба флага с одним установленным флагом, что, очевидно, ложно. Приведенный выше код оставит установленным только бит Flag1, если он вообще установлен, затем сравнивает этот результат с Flag1.

даже без [Flags] вы можете использовать что-то вроде этого

if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=0){
//..
}

или если у вас есть нулевое значение enum

if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=FlagTest.None){
//..
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top