2008-09-02 10 views
133

У меня есть перечисление флага ниже.Как сравнить флаги в C#?

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

Я не могу сделать утверждение if истинным.

FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2; 

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

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

+0

Поправьте меня, если я ошибаюсь, это 0 уместно быть используется как значение флага? – Roylee 2013-01-17 16:11:44

+3

@ Roylee: 0 приемлемо, и неплохо иметь флаг «None» или «Undefined», чтобы проверить отсутствие установленных флагов. Это отнюдь не требуется, но это хорошая практика. О важности, о которой нужно помнить, указал Леонид в его ответе. – Andy 2013-03-10 19:08:02

+4

@Roylee Настоятельно рекомендуется Microsoft предоставить флаг «Нет» со значением нуля. См. Http://msdn.microsoft.com/en-us/library/vstudio/ms229058(v=vs.100).aspx – ThatMatthew 2013-04-17 19:27:27

ответ

289

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

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

, который является более читабельным, ИМО.

The.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); 
} 
4

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

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

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

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

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

(testItem & FlagTest.Flag1) - побитовая операция И.

FlagTest.Flag1 эквивалентен 001 с перечислением ОП. Теперь предположим, что testItem имеет flag1 и flag2 (так это побитовое 101):

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

Что такое логика здесь? Почему предикат должен быть написан так? – 2014-03-25 15:42:13

4

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


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

21

Я установил способ расширения для этого: related question.

В основном:

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 

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

3

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

т.е.

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

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

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

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

testItem 
= flag1 | flag2 
= 001 | 010 
= 011 

Тогда, в если утверждение, левая часть сравнения,

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

И если полное утверждение (что будет истинным, если flag1 установлен в testItem),

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

еще один совет ... Никогда не делайте стандартный бинарный чек с флагом которого Значение равно «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); 
21

@ PHIL-Devaney

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

1

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

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

или если у вас есть нулевое значение перечисления в

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