다음이 있다고 가정 해 봅시다.
int susan = 2; //0010
int bob = 4; //0100
int karen = 8; //1000
메서드에 매개 변수로 10 (8 + 2)을 전달하고이를 수잔과 카렌을 의미하도록 디코딩하고 싶습니다.
10이 1010이라는 걸 알아
하지만 특정 비트가에서와 같이 확인되었는지 확인하는 논리를 어떻게 할 수 있습니까?
if (condition_for_karen) // How to quickly check whether effective karen bit is 1
지금 제가 생각할 수있는 것은 제가 전달한 숫자가
14 // 1110
12 // 1100
10 // 1010
8 // 1000
실제 시나리오에서 실제 비트 수가 많을 때 이것은 비현실적인 것 같습니다. 마스크를 사용하여 카렌의 조건을 충족하는지 여부를 확인하는 더 좋은 방법은 무엇입니까?
왼쪽으로 이동 한 다음 다시 오른쪽으로 이동 한 다음 다시 내가 관심있는 비트가 아닌 다른 비트로 이동하는 것을 생각할 수 있지만 이것은 지나치게 복잡해 보입니다.
답변
이를 수행하는 전통적인 방법은에 Flags
속성 을 사용하는 것입니다 enum
.
[Flags]
public enum Names
{
None = 0,
Susan = 1,
Bob = 2,
Karen = 4
}
그런 다음 다음과 같이 특정 이름을 확인합니다.
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;
논리적 인 비트 조합은 기억하기 어려울 수 있으므로 FlagsHelper
클래스 *를 사용하여 자신의 삶을 더 쉽게 만듭니다 .
// 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));
}
}
이렇게하면 위 코드를 다음과 같이 다시 작성할 수 있습니다.
Names names = Names.Susan | Names.Bob;
bool susanIsIncluded = FlagsHelper.IsSet(names, Names.Susan);
bool karenIsIncluded = FlagsHelper.IsSet(names, Names.Karen);
Karen
다음을 수행하여 세트에 추가 할 수도 있습니다 .
FlagsHelper.Set(ref names, Names.Karen);
Susan
비슷한 방법으로 제거 할 수 있습니다 .
FlagsHelper.Unset(ref names, Names.Susan);
* Forges가 지적했듯이 IsSet
위 의 방법 과 동일한 방법이 .NET 4.0에 이미 존재합니다 Enum.HasFlag
. 그러나 Set
및 Unset
메서드에는 동등한 항목이없는 것 같습니다. 그래서 저는이 수업에 약간의 장점이 있다고 말하고 싶습니다.
참고 : 열거 형을 사용하는 것은 이 문제를 해결 하는 일반적인 방법 일뿐 입니다. 위의 모든 코드를 완전히 번역하여 대신 int를 사용할 수 있으며 잘 작동합니다.
답변
if ( ( param & karen ) == karen )
{
// Do stuff
}
비트 ‘and’는 Karen을 “나타내는”비트를 제외한 모든 것을 마스킹합니다. 각 사람이 단일 비트 위치로 표시되는 한 간단한 방법으로 여러 사람을 확인할 수 있습니다.
if ( ( param & karen ) == karen )
{
// Do Karen's stuff
}
if ( ( param & bob ) == bob )
// Do Bob's stuff
}
답변
여기에는 마스크를 데이터베이스 열에 int로 저장하는 방법과 나중에 마스크를 복원하는 방법을 보여주는 예제가 포함되어 있습니다.
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;
답변
비트 마스크를 결합하려면 bitwise- 또는 . 결합하는 모든 값에 정확히 1 비트가있는 사소한 경우 (예 : 예)는 값을 추가하는 것과 동일합니다. 그러나 겹치는 비트가 있거나 ‘ing’은 케이스를 우아하게 처리합니다.
다음 과 같이 마스크를 사용하여 비트 마스크 와 값 을 디코딩하려면 :
if(val & (1<<1)) SusanIsOn();
if(val & (1<<2)) BobIsOn();
if(val & (1<<3)) KarenIsOn();
답변
쉬운 방법:
[Flags]
public enum MyFlags {
None = 0,
Susan = 1,
Alice = 2,
Bob = 4,
Eve = 8
}
플래그를 설정하려면 논리 “or”연산자를 사용하십시오 |
.
MyFlags f = new MyFlags();
f = MyFlags.Alice | MyFlags.Bob;
플래그가 포함되어 있는지 확인하려면 HasFlag
다음을 사용하십시오 .
if(f.HasFlag(MyFlags.Alice)) { /* true */}
if(f.HasFlag(MyFlags.Eve)) { /* false */}
답변
비트 마스크 대 개별 부울을 사용하는 또 다른 좋은 이유는 웹 개발자로서 한 웹 사이트를 다른 웹 사이트에 통합 할 때 자주 쿼리 문자열에 매개 변수 나 플래그를 보내야한다는 것입니다. 모든 플래그가 바이너리이면 여러 값을 bool로 보내는 것보다 단일 값을 비트 마스크로 사용하는 것이 훨씬 간단합니다. 데이터를 보내는 다른 방법 (GET, POST 등)이 있다는 것을 알고 있지만 쿼리 문자열의 간단한 매개 변수는 대부분 민감하지 않은 항목에 충분합니다. 외부 사이트와 통신하기 위해 쿼리 문자열에 128 개의 bool 값을 보내십시오. 이것은 또한 브라우저의 URL 쿼리 문자열에 제한을 적용하지 않는 추가 기능을 제공합니다.