Как сравнить флаги в С#?
У меня есть перечисление флага ниже.
[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.
}
Как я могу сделать это правдой?
Ответы
Ответ 1
В .NET 4 появился новый метод Enum.HasFlag. Это позволяет вам написать:
if ( testItem.HasFlag( FlagTest.Flag1 ) )
{
// Do Stuff
}
который является более читаемым, IMO.
Источник .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);
}
Ответ 2
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// Do something
}
(testItem & FlagTest.Flag1)
- побитовая операция И.
FlagTest.Flag1
эквивалентно 001
с перечислением OP. Теперь скажем, что testItem
имеет флаги1 и флаг2 (поэтому он побитовый 101
):
001
&101
----
001 == FlagTest.Flag1
Ответ 3
Для тех, у кого проблемы с визуализацией того, что происходит с принятым решением
(что и есть),
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// Do stuff.
}
testItem
(в соответствии с вопросом) определяется как
testItem
= flag1 | flag2
= 001 | 010
= 011
Затем в выражении if левая часть сравнения:
(testItem & flag1)
= (011 & 001)
= 001
И оператор full if (который принимает значение true, если flag1
установлен в testItem
),
(testItem & flag1) == flag1
= (001) == 001
= true
Ответ 4
Я установил метод расширения для этого: связанный вопрос.
В принципе:
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
Кстати, соглашение, которое я использую для перечислений, является единственным для стандартного, множественного числа для флагов. Таким образом, вы знаете из имени перечисления, может ли он содержать несколько значений.
Ответ 5
@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 мс для стандартной побитовой реализации.
Ответ 6
Еще один совет: никогда не делайте стандартную двоичную проверку с флагом, значение которого равно "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);
Ответ 7
if((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
...
}
Ответ 8
Для бит-операций вам нужно использовать побитовые операторы.
Это должно сделать трюк:
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// Do something,
// however This is never true.
}
Изменить: Исправлена ошибка, если я проверил - я снова вернулся к своим C/С++ (спасибо Райан Фарли за указание)
Ответ 9
Попробуйте следующее:
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// do something
}
В основном, ваш код спрашивает, совпадает ли с установленным оба флага, так как имеет один флаг, который, очевидно, является ложным. В приведенном выше коде останется только бит Flag1, если он установлен вообще, а затем сравнивает этот результат с Flag1.
Ответ 10
Относительно редактирования. Вы не можете сделать это правдой. Я предлагаю вам обернуть то, что вы хотите, в другой класс (или метод расширения), чтобы приблизиться к синтаксису, который вам нужен.
то есть.
public class FlagTestCompare
{
public static bool Compare(this FlagTest myFlag, FlagTest condition)
{
return ((myFlag & condition) == condition);
}
}
Ответ 11
даже без [Флаги], вы можете использовать что-то вроде этого
if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=0){
//..
}
или если у вас есть перечисление нулевого значения
if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=FlagTest.None){
//..
}