C#에서 플래그를 비교하는 방법은 무엇입니까?
-
09-06-2019 - |
문제
아래에 플래그 열거형이 있습니다.
[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
}
훨씬 더 읽기 쉽습니다. 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);
}
다른 팁
if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
// Do something
}
(testItem & 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 문(if가 true로 평가됨) flag1
에 설정되어 있습니다 testItem
),
(testItem & flag1) == flag1
= (001) == 001
= true
@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();
}
}
천만 번 이상의 반복을 수행하는 HasFlags 확장 메서드는 표준 비트 단위 구현의 27ms와 비교하여 무려 4793ms를 사용합니다.
이를 수행하기 위해 확장 방법을 설정했습니다. 관련 질문.
원래:
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"인 플래그를 사용하여 표준 바이너리 검사를 수행하지 마십시오.이 플래그에 대한 확인은 항상 true입니다.
[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는 ANYTHING & 0 항상 == 0이므로 항상 true입니다.
대신 입력 값이 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++ 방식으로 다시 돌아갔습니다(이를 지적한 Ryan Farley에게 감사드립니다).
편집에 관해서.당신은 그것을 사실로 만들 수 없습니다.필요한 구문에 더 가까워지기 위해 원하는 것을 다른 클래스(또는 확장 메서드)로 래핑하는 것이 좋습니다.
즉.
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){
//..
}
또는 0 값 열거형이 있는 경우
if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=FlagTest.None){
//..
}