[C#] 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.
}

어떻게하면 되나요?



답변

.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.Flag1001OP의 열거 형과 같습니다 . 이제 testItemFlag1과 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 문 ( flag1에 설정된 경우 true로 평가됨 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 확장 방법은 1 천만 회 이상 반복되는 표준 비트 구현의 27ms와 비교하여 무려 4793ms가 걸립니다.


답변

확장 방법을 설정했습니다 : 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

우연히 열거 형에 사용하는 규칙은 표준에 대해서는 단수, 플래그에는 복수입니다. 그렇게하면 열거 형 이름에서 여러 값을 보유 할 수 있는지 알 수 있습니다.


답변

충고 하나 더 … 값이 “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는 ANYTHING & 0은 항상 == 0이므로 항상 참입니다.


대신 입력 값이 0인지 간단히 확인해야합니다.

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


답변

if((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
...
}