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


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.