Soru C # içinde bir bit maskesi kullanma


Diyelim ki aşağıdakilere sahibim

int susan = 2; //0010
int bob = 4; //0100
int karen = 8; //1000

ve 10 (8 + 2) parametresini bir yönteme aktarıyorum ve bunu susan ve karen olarak çözmek istiyorum.

1010'un 1010 olduğunu biliyorum.

ancak belirli bir bitin nasıl kontrol edildiğini görmek için nasıl mantık yapabilirim

if (condition_for_karen) // How to quickly check whether effective karen bit is 1

Şu an tüm düşünebildiğim sayıyı kontrol etmektir.

14 // 1110
12 // 1100
10 // 1010
8 //  1000

Gerçek dünya senaryomda daha fazla sayıda gerçek bit olduğunda, bu pratik görünmüyor, maskenin sadece karen için gerekli koşulları karşılayıp karşılamadığımı kontrol etmek için daha iyi bir yolu nedir?

Sola kaymayı sonradan geri dönmeyi düşünebilirim, sonra sağa kayıyorum, sonra da ilgilendiğimden başka bitleri temizlemek için geri dönüyorum, ama bu da aşırı karmaşık görünüyor.


77
2017-07-16 02:08


Menşei


Sadece kullanım hakkında yorum yapmak zorunda kaldı. Eğer bit işlemleri yapıyorsanız, sadece bit manipülasyon operatörleri kullanmalısınız. yani, onu (8 + 2) olarak düşünün (8 + 2). - Jeff Mercado


Cevaplar:


Bunu yapmanın geleneksel yolu, Flags bir özellik enum:

[Flags]
public enum Names
{
    None = 0,
    Susan = 1,
    Bob = 2,
    Karen = 4
}

Sonra belirli bir adı aşağıdaki gibi kontrol edersiniz:

Names names = Names.Susan | Names.Bob;

// evaluates to true
bool susanIsIncluded = (names & Names.Susan) != Names.None;

// evaluates to false
bool karenIsIncluded = (names & Names.Karen) != Names.None;

Mantıklı bitsel kombinasyonlar hatırlamak zor olabilir, bu yüzden kendimle hayatı daha kolay hale getiriyorum. FlagsHelper sınıf*:

// The casts to object in the below code are an unfortunate necessity due to
// C#'s restriction against a where T : Enum constraint. (There are ways around
// this, but they're outside the scope of this simple illustration.)
public static class FlagsHelper
{
    public static bool IsSet<T>(T flags, T flag) where T : struct
    {
        int flagsValue = (int)(object)flags;
        int flagValue = (int)(object)flag;

        return (flagsValue & flagValue) != 0;
    }

    public static void Set<T>(ref T flags, T flag) where T : struct
    {
        int flagsValue = (int)(object)flags;
        int flagValue = (int)(object)flag;

        flags = (T)(object)(flagsValue | flagValue);
    }

    public static void Unset<T>(ref T flags, T flag) where T : struct
    {
        int flagsValue = (int)(object)flags;
        int flagValue = (int)(object)flag;

        flags = (T)(object)(flagsValue & (~flagValue));
    }
}

Bu, yukarıdaki kodu aşağıdaki gibi yeniden yazmamı sağlar:

Names names = Names.Susan | Names.Bob;

bool susanIsIncluded = FlagsHelper.IsSet(names, Names.Susan);

bool karenIsIncluded = FlagsHelper.IsSet(names, Names.Karen);

Not Ayrıca ekleyebilirim Karen Bunu yaparak sete:

FlagsHelper.Set(ref names, Names.Karen);

Ve ben çıkarabilirim Susan benzer bir yolla:

FlagsHelper.Unset(ref names, Names.Susan);

* Porges'in işaret ettiği gibi, IsSet Yukarıdaki yöntem .NET 4.0'da zaten var: Enum.HasFlag. Set ve Unset metotlar eşdeğer gibi görünmüyor; Bu yüzden hala bu sınıfın bir değeri olduğunu söyleyebilirim.


Not: Enums kullanarak sadece Konvansiyonel Bu problemin üstesinden gelmenin yolu. Yukarıdaki kodun tamamını ints kullanmak yerine tamamen çevirebilirsiniz ve aynı şekilde çalışır.


156
2017-07-16 02:17



Aslında çalışmak için ilk kod olan +1. Sen de yapabilirsin (names & Names.Susan) == Names.Susangerektirmeyen bir None. - Matthew Flaschen
@Matthew: Oh evet, iyi nokta. Sanırım her zaman tanımayı alışkanlık haline getiriyorum. None Ben tüm enumlar için değer, birçok senaryoda uygun buluyorum. - Dan Tao
Bu yerleşik olarak, yardım yöntemlerine ihtiyacın yok ... var susanIsIncluded = names.HasFlag(Names.Susan); - porges
@Porges: Vay, ne kadar özledim bilmiyorum ... bunu işaret ettiğin için teşekkürler! (Sadece .NET 4.0'dan itibaren mevcut gibi görünüyor, ancak ... ayrıca, hiçbir eşdeğer yoktur. Set yöntem. Yani, yardımcı yöntemler en azından olmadığını söyleyebilirim bütünüyle değersiz.) - Dan Tao
Not, kullanarak names.HasFlag(Names.Susan) gibi (names & Names.Susan) == Names.Susan her zaman böyle değil (names & Names.Susan) != Names.None. Örneğin eğer kontrol ederseniz names.HasFlag(Names.none) veya names.HasFlag(Names.Susan|Names.Karen) - A.B.Cade


if ( ( param & karen ) == karen )
{
  // Do stuff
}

Bitsel 've', Karen'ı temsil eden bit hariç her şeyi maskeleyecek. Her kişi tek bir bit konumuyla temsil edildiği sürece, birden fazla kişiyi basit bir şekilde kontrol edebilirsiniz:

if ( ( param & karen ) == karen )
{
  // Do Karen's stuff
}
if ( ( param & bob ) == bob )
  // Do Bob's stuff
}

19
2017-07-16 02:13





Maskeyi bir veritabanı sütununda int olarak nasıl saklayacağınızı ve daha sonra da maskeyi nasıl yeniden kuracağınızı gösteren bir örnek ekledim:

public enum DaysBitMask { Mon=0, Tues=1, Wed=2, Thu = 4, Fri = 8, Sat = 16, Sun = 32 }


DaysBitMask mask = DaysBitMask.Sat | DaysBitMask.Thu;
bool test;
if ((mask & DaysBitMask.Sat) == DaysBitMask.Sat)
    test = true;
if ((mask & DaysBitMask.Thu) == DaysBitMask.Thu)
    test = true;
if ((mask & DaysBitMask.Wed) != DaysBitMask.Wed)
    test = true;

// Store the value
int storedVal = (int)mask;

// Reinstate the mask and re-test
DaysBitMask reHydratedMask = (DaysBitMask)storedVal;

if ((reHydratedMask & DaysBitMask.Sat) == DaysBitMask.Sat)
    test = true;
if ((reHydratedMask & DaysBitMask.Thu) == DaysBitMask.Thu)
    test = true;
if ((reHydratedMask & DaysBitMask.Wed) != DaysBitMask.Wed)
    test = true;

9
2018-05-17 10:02



Benzer bir şey yaptım ama maskeyi tanımlarken Mon = Math.Power (2, 0), Salı = Math.Pow (2, 1), Wed = Math.Pow (2, 2) vb. ondalık dönüşüm için ikili olarak kullanılmayanlar için biraz daha açık. Maskelenen bitin kaydırılmasıyla bir boole sonucuna dönüştüğü için blind'ın da iyidir. - Analog Arsonist


Bit maskelerini birleştirmek için bitümlü kullanmak istersiniz.veya. Birleştirdiğiniz her değerin tam olarak 1 bitlik olduğu (örneğin örneğiniz) önemsiz durumda, bunları eklemenin karşılığıdır. Bununla birlikte, çakışan bitleriniz varsa, veyaOnlara göre davayı incelikle ele alır.

Bit maskelerini çözmek için ve değeriniz bir maskeyle, öyle ki:

if(val & (1<<1)) SusanIsOn();
if(val & (1<<2)) BobIsOn();
if(val & (1<<3)) KarenIsOn();

7
2017-07-16 02:13





Bir bit maskesi ile tekil bir bobinin kullanılması için gerçekten iyi bir neden, bir web geliştiricisi olarak, bir web sitesini bir diğerine entegre ederken, sık sık sorgulayıcıya parametreler veya bayraklar göndermemiz gerekir. Tüm bayraklarınız ikili olduğu sürece, bobinler olarak birden çok değer göndermekten çok, bir bit maskesi olarak tek bir değer kullanmanın çok daha basit bir hale gelmesini sağlar. Veri göndermek için başka yolların olduğunu biliyorum (GET, POST, vb.), Ancak querystring üzerindeki basit bir parametre çoğu zaman hassas olmayan öğeler için yeterlidir. Harici bir site ile iletişim kurmak için bir querystring'e 128 bool değeri göndermeyi deneyin. Bu ayrıca tarayıcılarda URL'leri sorgulamak için ek becerisi de vermez.


0
2018-02-03 19:01