Bayrak kombinasyonunun herhangi bir bayrağının ayarlanıp ayarlanmadığını nasıl kontrol edebilirim?


180

Diyelim ki bu numaram var:

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

Örneğin ABayarlanıp ayarlanmadığını kontrol etmek için bunu yapabilirim:

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

Birleşik bayrak sabitinin bayraklarından herhangi birinin aşağıdakilerden daha iyi ayarlanıp ayarlanmadığını kontrol etmenin daha kolay bir yolu var mı?

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

Örneğin &, bir şeyle değiştirebilir misiniz ?

Böyle ikili şeyler söz konusu olduğunda çok kararlı değil ...


Hepsi 'All = A | B | C '?
stevehipwell

4
AB | C, A | B | C çünkü AB A | B önce.
Daniel Brückner

1
@Daniel Brückner - Eşdeğerdir, ancak daha az okunabilir. Özellikle enum genişletilmişse.
stevehipwell

Doğru. Daha iyi okumak için değiştirebilirim.
Svish

Yanıtlar:


145

AB harfinde harflerden herhangi birinin olup olmadığını öğrenmek için AND & operatörünü kullanmalısınız. Gibi bir şey:

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

2
Görebildiğim kadarıyla bu işi yapıyor. Ve en açık yorumlar vardı. Etrafta parantez olmadan derlenmez letter & Letters.AB. Orada düzenledi.
Svish

Ayrıca Letters.None, bir tanesini tanıtırsam, 0sihirli sayı ile daha az karşılaştırmak için bunu takas edebileceğinizi varsayalım ?
Svish

Elbette. Ama AND ile 0'ın karşılaştırmasının kesinlikle sihirli bir sayı olarak düşünülebileceğini düşünmüyorum.
yeyeyerman

9
Ayrıca stackoverflow.com/questions/40211/how-to-compare-flags-in-c kontrol aksine o eşitse ilgili öğeye karşı bu kontroller olarak önerilen bir cevaptır 0
dan richardson

@danrichardson, kesin öğenin kontrolüyle ilgili sorun , OP değerinin bir parçası olan bileşik değerinin bir kısmı (A veya B) ayarlandığında durumu ortadan kaldırmasıdır .
Tom Lint

181

.NET 4'te Enum.HasFlag yöntemini kullanabilirsiniz :

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);   
   }
}

Örnekte aşağıdaki çıktı gösterilmektedir:

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

14
Bu OP sorusunu ele almaz. Herhangi bir bayrağın ayarlanıp ayarlanmadığını belirlemek için hala && birden fazla HasFlag işlemi yapmanız gerekir . Yani soru şu petsInFamilyya bir Pet.Dog || Pet.Cat?
GoClimbColorado


59

Ben böyle şeyler yazmak için uzatma yöntemleri kullanın:

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

İşte kod:

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
Bunu bir şöyle sıkı biraz yapabiliriz: where T : struct, IConvertible. Aksi takdirde büyük kod!
Hamish Grubijan

@HamishGrubijan, iyi bir nokta ... ve numaralandırmalar da IFormattable ve IComparable uygular. Bununla birlikte, tüm sayısal türler de bu arayüzleri uygular, bu yüzden onları hariç tutmak yeterli değildir
Thomas Levesque

Paylaştığınız için teşekkürler, ancak her zaman enum'u kontrol etmeniz gerekmez. IsFlagSet(this Enum value, Enum flag)yeterlidir.
djmj

34

.NET 4 veya sonraki sürümlerde HasFlag yöntemi vardır .

if(letter.HasFlag(Letters.AB))
{
}

26

.NET 4 veya üstünü HasFlag () yöntemini kullanmaktan daha yüksek

örnekler

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

ile aynı

letter.HasFlag(Letters.AB)

Emin olun bitwise OR"ikisi de ayarlanmalıdır" ve hiç değil mi?
Parantez

1
bitwise ORdeğerleri birleştirir, böylece 1000 | 0010 1010 olur veya her ikisi birden ayarlanır
Armando

13

Gerçekten sizi rahatsız ediyorsa, böyle bir işlev yazabilirsiniz:

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
Satır return (value & flag) == flag;derlenmiyor: "Operatör" & "," T "ve" T "türündeki işlenenlere uygulanamaz .
Fredrik Mörk

1
huşu: Soru ikili işlemlerle ilgili değil, soru C #'daki bitmask ile ilgili işlemlerin sözdizimini basitleştirmekle ilgiliydi. Stackoverflow üzerinde zaten mükemmel ikili operasyonla ilgili birçok soru ve cevap var, onları her yerde yeniden göndermeye gerek yok.
Tamas Czinege

İkili işlemlere aşina olmayanların aşina olmalarını tavsiye etmeliyim, çünkü yukarıda gizlemek için iskele aslında bence daha az okunabilir hale getiriyor. ;-) insanların tercihlerini oy veriyor ve buna saygı zorunda Tabii aşağıda benim 'ham' çözüm şu anda, o kadar iyi bu çözümün puanına kıyasla çıkmıyor Of
Will

10

Örneğin AB'nin ayarlanıp ayarlanmadığını kontrol etmek için bunu yapabilirim:

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

Birleşik bayrak sabitinin bayraklarından herhangi birinin aşağıdakilerden daha iyi ayarlanıp ayarlanmadığını kontrol etmenin daha kolay bir yolu var mı?

Bu, hem A hem de B'nin ayarlandığını kontrol eder ve başka bayrakların ayarlanıp ayarlanmadığını yoksayar.

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

Bu kontroller , ya A veya B ayarlanır ve yoksaydıklarınız başka bayraklar set olsun veya olmasın.

Bu basitleştirilebilir:

if(letter & Letters.AB)

İşte ikili işlemler için C; bunu C # 'a uygulamak basit olmalıdır:

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));

Bu arada, sorunun örneğindeki değişkenin isimlendirilmesi, tek bir harfi temsil ettiği anlamına gelebilecek tekil "harf" tir; örnek kod, bir dizi olası harfin ve birden çok değere izin verildiğini açıkça gösterir, bu nedenle değişken 'harfleri' yeniden adlandırmayı düşünün.


Olmaz anything_and_a, a_and_or_c_and_anything_elseve both_ac_and_anything_elsedaima doğru? yoksa burada bir şey mi kaçırıyorum?
Svish

Bu durumda, hangi bayrakların başlatıldığını görebilirsiniz. Ancak, bayraklar A içermiyorsa, (bayraklar ve A) 0 olur, bu yanlıştır. both_ac_and_anything_else, A ve C'nin her ikisinin de ayarlanmasını sağlar, ancak ayarlanan diğer bayrakları da yok sayar (örneğin, B ayarlı olsun ya da olmasın doğru).
Will

Hm, bunlardan bazıları sayı olarak bitiyor ve C # 'da boole değil. Onları boole'ye nasıl dönüştürürdünüz?
Svish

Sizin için dolaylı olarak dönüştürülmedi mi? Sıfır, 'false' değerine eşittir ve diğer tüm değerler 'true' değeridir.
Will

4

Peki ya

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

?


Evet! Bu, A ve B değerlerini filtreleyecek ve C'nin dahil olup olmadığını göz ardı edecektir. Yani> 0 ise, aynı zamanda A veya B veya AB'dir.
huşu

3
Bu% 100 imzalı değerlerle çalışmaz. ! = 0 bu nedenle> 0'dan daha iyidir.
stevehipwell

4

EnumTürleri üzerinde bir kontrol gerektirmeyen basit bir uzantı yöntemi oluşturdum :

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

Aynı zamanda sıfırlanabilir numaralandırmalarda da çalışır. Standart HasFlagyöntem değil, bu yüzden de kapsayacak bir uzantı oluşturdu.

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

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

Basit bir test:

[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));
}

Zevk almak!


4

Burada çok cevap var ama sanırım Bayraklar ile bunu yapmanın en deyimsel yolu Letters.AB.HasFlag (mektup) veya (Letters.A | Letters.B) olurdu. Zaten Letters.AB var. letter.HasFlag (Letters.AB) yalnızca her ikisine de sahipse çalışır.


3

Bu senin için işe yarar mı?

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

Saygılarımızla,

Sebastiaan


1

Bu uzantı yöntemini, enum'da, her türden numaralandırma için kullanabilirsiniz:

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) { }

Benim yaptığım aynı hatayla karşılaşabilirsiniz - A veya B'nin ayarlanıp ayarlanmadığını kontrol etmek istiyor ama C'yi görmezden geliyor
Daniel Brückner


Bu, birleşik bir numaralandırmadan herhangi birinin ayarlanıp ayarlanmadığını değil, hepsinin ayarlandığını kontrol eder.
Svish

0

Değerin sıfır olup olmadığını kontrol edebilirsiniz.

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

Ama sıfır değeri ile yeni bir numaralandırma değeri tanıtmak ve bu numaralandırma değeri ile karşılaştırmak (mümkünse numaralandırma değiştirmek gerekir çünkü) karşılaştırmak için daha iyi bir çözüm düşünün.

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

if (letter != Letters.None) { }

GÜNCELLEME

Soruyu kaçırmayın - ilk öneri düzeltildi ve ikinci öneri yok sayıldı.



0

Herhangi bir bitin kontrol edilmesi için işe yarayacağını görebildiğim iki yaklaşım var.

Yaklaşım A

if (letter != 0)
{
}

Bu , tanımlı olmayanlar da dahil olmak üzere tüm bitleri kontrol etmeyi düşünmediğiniz sürece çalışır !

Yaklaşım B

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

Bu, yalnızca Harfler olduğu sürece tanımlanan bitleri kontrol eder.Tüm olası tüm bitleri temsil eder.

Belirli bitler için (bir veya daha fazla set), Harfleri değiştirerek Yaklaşım B'yi kullanın.Tümünü kontrol etmek istediğiniz bitlerle (aşağıya bakın).

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

Benim yaptığım hata ile aynı olabilir - A veya B'nin ayarlanıp ayarlanmadığını kontrol etmek istiyor ama C'yi görmezden geliyor
Daniel Brückner

-1

Üzgünüm, ama VB'de göstereceğim :)

   <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 Yani numaralandırma 1 + 4 içeriyor

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.