Как сравнить флаги в C#?



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



[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.
}


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

681   11  

11 ответов:

в .NET 4 появился новый метод перечисление.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 & FlagTest.Flag1) является побитовым и операция.

FlagTest.Flag1 эквивалентно 001 с перечислением ОП. Теперь давайте скажем 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

и в полном объеме, если заявление (которое возвращает значение true, если flag1 находится в testItem),

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

@phil-devaney

обратите внимание, что за исключением простейших случаев перечисление.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 всегда будет истинным как что-либо & 0 всегда == 0.


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

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

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


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

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

Это должно сделать трюк:

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

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

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

т. е.

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

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

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

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

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

Comments

    Ничего не найдено.