[C#] 열거 형에서 가장 일반적인 C # 비트 연산

내 인생에서 비트 필드에서 비트를 설정, 삭제, 토글 또는 테스트하는 방법을 기억할 수 없습니다. 확실하지 않거나 거의 필요하지 않기 때문에 혼합합니다. 따라서 “비트 치트 시트”가 있으면 좋을 것입니다.

예를 들면 다음과 같습니다.

flags = flags | FlagsEnum.Bit4;  // Set bit 4.

또는

if ((flags & FlagsEnum.Bit4)) == FlagsEnum.Bit4) // Is there a less verbose way?

[Flags] 열거 형을 사용하여 C # 구문에서 다른 모든 일반적인 작업의 예를 제공 할 수 있습니까?



답변

이 확장에 대해 더 많은 작업을 수행했습니다. 여기에서 코드를 찾을 수 있습니다.

나는 자주 사용하는 System.Enum을 확장하는 확장 방법을 썼습니다 … 방탄이라고 주장하지는 않지만 도움이되었습니다 … 댓글이 삭제되었습니다 …

namespace Enum.Extensions {

    public static class EnumerationExtensions {

        public static bool Has<T>(this System.Enum type, T value) {
            try {
                return (((int)(object)type & (int)(object)value) == (int)(object)value);
            }
            catch {
                return false;
            }
        }

        public static bool Is<T>(this System.Enum type, T value) {
            try {
                return (int)(object)type == (int)(object)value;
            }
            catch {
                return false;
            }
        }


        public static T Add<T>(this System.Enum type, T value) {
            try {
                return (T)(object)(((int)(object)type | (int)(object)value));
            }
            catch(Exception ex) {
                throw new ArgumentException(
                    string.Format(
                        "Could not append value from enumerated type '{0}'.",
                        typeof(T).Name
                        ), ex);
            }
        }


        public static T Remove<T>(this System.Enum type, T value) {
            try {
                return (T)(object)(((int)(object)type & ~(int)(object)value));
            }
            catch (Exception ex) {
                throw new ArgumentException(
                    string.Format(
                        "Could not remove value from enumerated type '{0}'.",
                        typeof(T).Name
                        ), ex);
            }
        }

    }
}

그런 다음 다음과 같이 사용됩니다

SomeType value = SomeType.Grapes;
bool isGrapes = value.Is(SomeType.Grapes); //true
bool hasGrapes = value.Has(SomeType.Grapes); //true

value = value.Add(SomeType.Oranges);
value = value.Add(SomeType.Apples);
value = value.Remove(SomeType.Grapes);

bool hasOranges = value.Has(SomeType.Oranges); //true
bool isApples = value.Is(SomeType.Apples); //false
bool hasGrapes = value.Has(SomeType.Grapes); //false


답변

.NET 4에서는 다음과 같이 작성할 수 있습니다.

flags.HasFlag(FlagsEnum.Bit4)


답변

관용구는 비트 또는 같음 연산자를 사용하여 비트를 설정하는 것입니다.

flags |= 0x04;

비트를 정리하기 위해 관용구는 비트 단위와 부정을 사용하는 것입니다.

flags &= ~0x04;

때때로 당신은 당신의 비트를 식별하는 오프셋을 가지고 있고, 관용구는 이것을 왼쪽 시프트와 결합하여 사용하는 것입니다 :

flags |= 1 << offset;
flags &= ~(1 << offset);


답변

D

가장 간단한 경우를 제외하고 Enum.HasFlag는 코드를 수동으로 작성하는 것과 비교하여 성능이 저하됩니다. 다음 코드를 고려하십시오.

[Flags]
public enum TestFlags
{
    One = 1,
    Two = 2,
    Three = 4,
    Four = 8,
    Five = 16,
    Six = 32,
    Seven = 64,
    Eight = 128,
    Nine = 256,
    Ten = 512
}


class Program
{
    static void Main(string[] args)
    {
        TestFlags f = TestFlags.Five; /* or any other enum */
        bool result = false;

        Stopwatch s = Stopwatch.StartNew();
        for (int i = 0; i < 10000000; i++)
        {
            result |= f.HasFlag(TestFlags.Three);
        }
        s.Stop();
        Console.WriteLine(s.ElapsedMilliseconds); // *4793 ms*

        s.Restart();
        for (int i = 0; i < 10000000; i++)
        {
            result |= (f & TestFlags.Three) != 0;
        }
        s.Stop();
        Console.WriteLine(s.ElapsedMilliseconds); // *27 ms*        

        Console.ReadLine();
    }
}

HasFlags 확장 방법은 1 천만 회 이상 반복되는 표준 비트 구현의 27ms와 비교하여 무려 4793ms가 걸립니다.


답변

.NET의 내장 플래그 열거 연산은 불행히도 상당히 제한적입니다. 대부분의 경우 사용자는 비트 연산 논리를 알아 내야합니다.

.NET 4에는 사용자의 코드를 단순화하는 데 도움 HasFlagEnum되는 방법 이 추가 되었지만 불행히도 많은 문제가 있습니다.

  1. HasFlag 주어진 열거 형뿐만 아니라 모든 유형의 열거 형 값 인수를 허용하므로 형식이 안전하지 않습니다.
  2. HasFlag값에 열거 형 값 인수가 제공하는 플래그가 모두 있는지 또는 없는지 여부를 확인하는 것이 모호합니다. 그건 그렇고
  3. HasFlag 복싱이 필요하기 때문에 할당이 느려져 가비지 수집이 증가합니다.

플래그 열거 형에 대한 .NET의 제한된 지원으로 인해 이러한 각 문제를 해결하고 플래그 열거 형을 훨씬 쉽게 처리 할 수 있는 OSS 라이브러리 Enums.NET 을 작성했습니다 .

다음은 .NET 프레임 워크를 사용하는 동등한 구현과 함께 제공되는 일부 조작입니다.

플래그 결합

.그물             flags | otherFlags

Enums.NET flags.CombineFlags(otherFlags)


깃발 제거

.그물             flags & ~otherFlags

Enums.NET flags.RemoveFlags(otherFlags)


공통 깃발

.그물             flags & otherFlags

Enums.NET flags.CommonFlags(otherFlags)


토글 플래그

.그물             flags ^ otherFlags

Enums.NET flags.ToggleFlags(otherFlags)


모든 플래그가 있음

.NET             (flags & otherFlags) == otherFlags 또는flags.HasFlag(otherFlags)

Enums.NET flags.HasAllFlags(otherFlags)


플래그가 있습니다

.그물             (flags & otherFlags) != 0

Enums.NET flags.HasAnyFlags(otherFlags)


깃발 얻기

.그물

Enumerable.Range(0, 64)
  .Where(bit => ((flags.GetTypeCode() == TypeCode.UInt64 ? (long)(ulong)flags : Convert.ToInt64(flags)) & (1L << bit)) != 0)
  .Select(bit => Enum.ToObject(flags.GetType(), 1L << bit))`

Enums.NET flags.GetFlags()


이러한 개선 사항을 .NET Core에 통합하고 궁극적으로 전체 .NET Framework를 얻으려고합니다. 내 제안을 여기서 확인할 수 있습니다 .


답변

비트 0이 LSB라고 가정하고 C ++ 구문은 플래그가 부호가 없다고 가정합니다.

설정되어 있는지 확인하십시오.

flags & (1UL << (bit to test# - 1))

설정되어 있지 않은지 확인하십시오.

invert test !(flag & (...))

세트:

flag |= (1UL << (bit to set# - 1))

명확한:

flag &= ~(1UL << (bit to clear# - 1))

비녀장:

flag ^= (1UL << (bit to set# - 1))


답변

최상의 성능과 쓰레기를 없애려면 다음을 사용하십시오.

using System;
using T = MyNamespace.MyFlags;

namespace MyNamespace
{
    [Flags]
    public enum MyFlags
    {
        None = 0,
        Flag1 = 1,
        Flag2 = 2
    }

    static class MyFlagsEx
    {
        public static bool Has(this T type, T value)
        {
            return (type & value) == value;
        }

        public static bool Is(this T type, T value)
        {
            return type == value;
        }

        public static T Add(this T type, T value)
        {
            return type | value;
        }

        public static T Remove(this T type, T value)
        {
            return type & ~value;
        }
    }
}