플래그 조합의 플래그가 설정되어 있는지 확인하는 방법은 무엇입니까?


180

이 열거 형이 있다고 가정 해 봅시다.

[Flags]
enum Letters
{
     A = 1,
     B = 2,
     C = 4,
     AB = A | B,
     All = A | B | C,
}

예를 들어 AB설정되어 있는지 확인하려면 다음을 수행하십시오.

if((letter & Letters.AB) == Letters.AB)

결합 플래그 상수의 플래그가 다음보다 설정되어 있는지 확인하는 간단한 방법이 있습니까?

if((letter & Letters.A) == Letters.A || (letter & Letters.B) == Letters.B)

예를 들어와 &무언가 를 바꿀 수 있습니까?

이진 바이너리에 관해서는 너무 안정적이지 않습니다 ...


모두 'All = A | B | 씨'?
— stevehipwell

4
AB | C는 A | B | AB가 A로 정의 되었기 때문에 C | 전에 B.
— Daniel Brückner

1
@Daniel Brückner-동일하지만 읽기 쉽지 않습니다. 특히 열거 형이 확장 된 경우.
— stevehipwell

진실. 더 나은 독서를 위해 그것을 바꿀 수 있습니다.
— Svish

답변:


145

AB에 문자가 있는지 확인하려면 AND & 연산자를 사용해야합니다 . 다음과 같은 것 :

if ((letter & Letters.AB) != 0)
{
    // Some flag (A,B or both) is enabled
}
else
{
    // None of them are enabled
}

2
내가 볼 수있는 한, 이것은 일을합니다. 그리고 가장 명확한 의견이있었습니다. 주위에 괄호가 없으면 컴파일되지 않습니다 letter & Letters.AB. 거기에서 편집했습니다.
— Svish

또한을 소개했다면 마술 숫자와 비교하기가 덜 Letters.None하기 0위해 이것을 바꿀 수 있다고 가정 합니까?
— Svish

물론이야. 그러나 0과 AND를 비교하는 것이 마술 숫자라고 생각할 수는 없다고 생각합니다.
— yeyeyerman

9
또한 stackoverflow.com/questions/40211/how-to-compare-flags-in-c는 체크 반대가 동일한 경우 해당 항목에 대하여 그 확인으로 권장 대답은 0
— 댄 리차드슨

@danrichardson 정확한 항목을 검사 할 때 의 문제는 복합 값 의 일부 가 설정되어 있을 때 (A 또는 B 중 하나) OP가 원하지 않는 경우를 제거한다는 것 입니다.
— Tom Lint

181

.NET 4에서는 Enum.HasFlag 메서드를 사용할 수 있습니다 .

using System;

[Flags] public enum Pet {
   None = 0,
   Dog = 1,
   Cat = 2,
   Bird = 4,
   Rabbit = 8,
   Other = 16
}

public class Example
{
   public static void Main()
   {
      // Define three families: one without pets, one with dog + cat and one with a dog only
      Pet[] petsInFamilies = { Pet.None, Pet.Dog | Pet.Cat, Pet.Dog };
      int familiesWithoutPets = 0;
      int familiesWithDog = 0;

      foreach (Pet petsInFamily in petsInFamilies)
      {
         // Count families that have no pets. 
         if (petsInFamily.Equals(Pet.None))
            familiesWithoutPets++;
         // Of families with pets, count families that have a dog. 
         else if (petsInFamily.HasFlag(Pet.Dog))
            familiesWithDog++;
      }
      Console.WriteLine("{0} of {1} families in the sample have no pets.", 
                        familiesWithoutPets, petsInFamilies.Length);   
      Console.WriteLine("{0} of {1} families in the sample have a dog.", 
                        familiesWithDog, petsInFamilies.Length);   
   }
}

예제는 다음 출력을 표시합니다.

//       1 of 3 families in the sample have no pets. 
//       2 of 3 families in the sample have a dog.

14
이것은 OP 질문을 다루지 않습니다. 경우 당신은 여전히 && 여러 HasFlag 작업이 결정해야한다 어떤 플래그가 설정됩니다. 그래서 질문은 않는 것입니다 petsInFamily중 하나가 Pet.Dog || Pet.Cat?
— GoClimbColorado

1
씨 스키트의 명확한 답변을 참조하십시오 ... HasFlags 다중
— GoClimbColorado

59

나는 확장 메소드를 사용하여 다음과 같은 것을 작성합니다.

if (letter.IsFlagSet(Letter.AB))
    ...

코드는 다음과 같습니다.

public static class EnumExtensions
{
    private static void CheckIsEnum<T>(bool withFlags)
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName));
        if (withFlags && !Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))
            throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName));
    }

    public static bool IsFlagSet<T>(this T value, T flag) where T : struct
    {
        CheckIsEnum<T>(true);
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flag);
        return (lValue & lFlag) != 0;
    }

    public static IEnumerable<T> GetFlags<T>(this T value) where T : struct
    {
        CheckIsEnum<T>(true);
        foreach (T flag in Enum.GetValues(typeof(T)).Cast<T>())
        {
            if (value.IsFlagSet(flag))
                yield return flag;
        }
    }

    public static T SetFlags<T>(this T value, T flags, bool on) where T : struct
    {
        CheckIsEnum<T>(true);
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flags);
        if (on)
        {
            lValue |= lFlag;
        }
        else
        {
            lValue &= (~lFlag);
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }

    public static T SetFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, true);
    }

    public static T ClearFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, false);
    }

    public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct
    {
        CheckIsEnum<T>(true);
        long lValue = 0;
        foreach (T flag in flags)
        {
            long lFlag = Convert.ToInt64(flag);
            lValue |= lFlag;
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }

    public static string GetDescription<T>(this T value) where T : struct
    {
        CheckIsEnum<T>(false);
        string name = Enum.GetName(typeof(T), value);
        if (name != null)
        {
            FieldInfo field = typeof(T).GetField(name);
            if (field != null)
            {
                DescriptionAttribute attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
                if (attr != null)
                {
                    return attr.Description;
                }
            }
        }
        return null;
    }
}

1
다음과 같이 조금 더 조일 수 where T : struct, IConvertible있습니다. 그렇지 않으면 훌륭한 코드!
— Hamish Grubijan

@HamishGrubijan, 좋은 지적 ... 및 열거 형도 IFormattable 및 IComparable을 구현합니다. 그러나 모든 숫자 유형은 이러한 인터페이스도 구현하므로이를 제외하는 것만으로는 충분하지 않습니다.
— Thomas Levesque

공유해 주셔서 감사하지만 항상 열거 형을 확인할 필요는 없습니다. IsFlagSet(this Enum value, Enum flag)충분하다.
— djmj


26

HasFlag () 메서드를 사용하는 것보다 .NET 4 이상을 사용할 수있는 경우

예

letter.HasFlag(Letters.A | Letters.B) // both A and B must be set

와 동일

letter.HasFlag(Letters.AB)

확실 bitwise OR하게 "둘 다 설정해야"합니까?
— 브래킷

1
bitwise OR값을 결합하므로 1000 | 0010은 1010이되거나 두 세트 모두
— Armando

13

정말 귀찮다면 다음과 같은 함수를 작성할 수 있습니다.

public bool IsSet(Letters value, Letters flag)
{
    return (value & flag) == flag;
}

if (IsSet(letter, Letters.A))
{
   // ...
}

// If you want to check if BOTH Letters.A and Letters.B are set:
if (IsSet(letter, Letters.A & Letters.B))
{
   // ...
}

// If you want an OR, I'm afraid you will have to be more verbose:
if (IsSet(letter, Letters.A) || IsSet(letter, Letters.B))
{
   // ...
}

1
행 return (value & flag) == flag;이 컴파일되지 않습니다 : "연산자 '&'은 (는) 'T'및 'T'유형의 피연산자에 적용 할 수 없습니다 . "
— Fredrik Mörk

1
경외감 : 이진 연산에 대한 문제는 아니고 C #에서 비트 마스크 관련 연산의 구문을 단순화하는 것에 관한 문제였습니다. 이진 연산 관련 우수한 질문과 답변이 이미 많이 있으며, 어디에서나 다시 게시 할 필요가 없습니다.
— Tamas Czinege

이진 연산에 익숙하지 않은 사람들은 익숙해 지도록 권장해야합니다. 비계를 숨기면 비계가 실제로 읽기가 훨씬 어렵습니다. 물론 아래의 '원시'솔루션은 현재이 솔루션의 점수와 비교하여 잘 수행되지 않으므로 사람들이 선호도에 투표하고 있습니다. ;-)
— Will

10

예를 들어 AB가 설정되어 있는지 확인하려면 다음과 같이하십시오.

if ((문자 및 Letters.AB) == Letters.AB)

결합 플래그 상수의 플래그가 다음보다 설정되어 있는지 확인하는 간단한 방법이 있습니까?

이 있는지 검사 모두 다른 플래그 여부와 B가 설정되고, 무시가 설정된다.

if((letter & Letters.A) == Letters.A || (letter & Letters.B) == Letters.B)

이 검사 중 A 또는 B가 설정되고, 무시는 다른 플래그가 설정되어 있는지의 여부.

다음과 같이 단순화 할 수 있습니다.

if(letter & Letters.AB)

이진 연산을위한 C는 다음과 같습니다. 이것을 C #에 적용하는 것이 간단해야합니다.

enum {
     A = 1,
     B = 2,
     C = 4,
     AB = A | B,
     All = AB | C,
};

int flags = A|C;

bool anything_and_a = flags & A;

bool only_a = (flags == A);

bool a_and_or_c_and_anything_else = flags & (A|C);

bool both_ac_and_anything_else = (flags & (A|C)) == (A|C);

bool only_a_and_c = (flags == (A|C));

덧붙여서, 질문의 예에서 변수의 이름은 단수의 'letter'입니다. 이것은 단일 문자만을 의미 할 수도 있습니다. 예제 코드는 가능한 문자 세트와 여러 값이 허용됨을 분명히하므로 변수 'letters'의 이름을 바꾸는 것이 좋습니다.


하지 않을까요 anything_and_a, a_and_or_c_and_anything_else그리고 both_ac_and_anything_else항상 진실? 아니면 여기에 뭔가 빠졌습니까?
— Svish

이 경우 어떤 플래그가 초기화되었는지 확인할 수 있습니다. 그러나 플래그에 A가 포함되어 있지 않으면 (flags & A)는 0이되며 이는 거짓입니다. both_ac_and_anything_else는 A와 C가 모두 설정되도록하지만 다른 플래그도 무시합니다 (예 : B 설정 여부에 관계없이).
— Will

흠, 그중 일부는 숫자로 끝나고 C #에서는 부울하지 않습니다. 그것들을 어떻게 부울로 변환 하시겠습니까?
— Svish

암시 적으로 당신을 위해 변환되지 않았습니까? 0은 'false'와 같고 다른 모든 값은 'true'입니다.
— Will

4

어때요?

if ((letter & Letters.AB) > 0)

?


예! 그러면 A 및 B 값이 필터링되고 C가 포함되어 있으면 무시됩니다. 따라서> 0이면 A 또는 B 또는 AB이기도합니다.
— awe

3
부호있는 값으로는 100 % 작동하지 않습니다. 이러한 이유로! = 0이> 0보다 낫습니다.
— stevehipwell

4

Enum유형을 확인하지 않아도되는 간단한 확장 방법을 만들었습니다 .

public static bool HasAnyFlag(this Enum value, Enum flags)
{
    return
        value != null && ((Convert.ToInt32(value) & Convert.ToInt32(flags)) != 0);
}

또한 nullable 열거 형에서도 작동합니다. 표준 HasFlag방법은 그렇지 않으므로 확장을 만들었습니다.

public static bool HasFlag(this Enum value, Enum flags)
{
    int f = Convert.ToInt32(flags);

    return
        value != null && ((Convert.ToInt32(value) & f) == f);
}

간단한 테스트 :

[Flags]
enum Option
{
    None = 0x00,
    One = 0x01,
    Two = 0x02,
    Three = One | Two,
    Four = 0x04
}

[TestMethod]
public void HasAnyFlag()
{
    Option o1 = Option.One;
    Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));
    Assert.AreEqual(false, o1.HasFlag(Option.Three));

    o1 |= Option.Two;
    Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));
    Assert.AreEqual(true, o1.HasFlag(Option.Three));
}

[TestMethod]
public void HasAnyFlag_NullableEnum()
{
    Option? o1 = Option.One;
    Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));
    Assert.AreEqual(false, o1.HasFlag(Option.Three));

    o1 |= Option.Two;
    Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));
    Assert.AreEqual(true, o1.HasFlag(Option.Three));
}

즐겨!


4

여기에 많은 답변이 있지만 플래그 로이 작업을 수행하는 가장 관용적 인 방법은 Letters.AB.HasFlag (letter) 또는 (Letters.A | Letters.B)입니다 .HasFlag (letter) 이미 Letters.AB가 있습니다. letter.HasFlag (Letters.AB)는 둘 다있는 경우에만 작동합니다.


3

이것이 당신을 위해 작동합니까?

if ((letter & (Letters.A | Letters.B)) != 0)

문안 인사,

세바스티안


1

열거 형에서 모든 유형의 열거 형에 대해이 확장 방법을 사용할 수 있습니다.

public static bool IsSingle(this Enum value)
{
    var items = Enum.GetValues(value.GetType());
    var counter = 0;
    foreach (var item in items)
    {
        if (value.HasFlag((Enum)item))
        {
            counter++;
        }
        if (counter > 1)
        {
            return false;
        }
    }
    return true;
}

0
if((int)letter != 0) { }

당신은 내가했던 것과 같은 실수 일 것입니다 – 그는 A 또는 B가 설정되어 있는지 확인하고 싶지만 C를 무시하고 싶습니다.
— Daniel Brückner

열거 형을 0에 대해 검사하는 경우 캐스트가 필요하지 않습니다.
— stevehipwell

이렇게하면 결합 된 열거 형이 설정되어 있는지가 아니라 모두 설정되었는지 확인합니다.
— Svish

0

값이 0이 아닌지 확인할 수 있습니다.

if ((Int32)(letter & Letters.AB) != 0) { }

그러나 값이 0 인 새 열거 값을 도입 하고이 열거 값을 다시 비교하는 것이 더 나은 솔루션이라고 생각합니다 (가능한 경우 열거를 수정할 수 있어야하기 때문에).

[Flags]
enum Letters
{
    None = 0,
    A    = 1,
    B    = 2,
    C    = 4,
    AB   =  A | B,
    All  = AB | C
}

if (letter != Letters.None) { }

최신 정보

질문을 잘못 읽음-첫 번째 제안을 수정하고 두 번째 제안을 무시하십시오.


열거 형을 0에 대해 검사하는 경우 캐스트가 필요하지 않습니다.
— stevehipwell

0

비트가 설정되어 있는지 확인하는 데 도움이되는 두 가지 공격이 있습니다.

Aproach A

if (letter != 0)
{
}

이것은 정의되지 않은 비트를 포함하여 모든 비트를 검사하는 것을 신경 쓰지 않는 한 작동합니다 !

어프로치 B

if ((letter & Letters.All) != 0)
{
}

Letters.All이 가능한 모든 비트를 나타내는 한 정의 된 비트 만 검사합니다.

특정 비트 (하나 이상 세트)의 경우 문자를 대체하는 Aproach B를 사용하십시오. 모두 확인하려는 비트로 아래를 참조하십시오.

if ((letter & Letters.AB) != 0)
{
}

당신은 내가했던 것과 같은 실수일지도 모릅니다. 그는 A 또는 B가 설정되어 있는지 확인하고 싶지만 C를 무시하려고합니다.
— Daniel Brückner

-1

미안하지만 VB로 표시합니다 :)

   <Flags()> Public Enum Cnt As Integer
        None = 0
        One = 1
        Two = 2
        Three = 4
        Four = 8    
    End Enum

    Sub Test()
    Dim CntValue As New Cnt
    CntValue += Cnt.One
    CntValue += Cnt.Three
    Console.WriteLine(CntValue)
    End Sub

CntValue = 5 열거 형에 1 + 4가 들어 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.