플래그 열거 형을 보유하는 변수가있는 경우 어떻게 든 특정 변수의 비트 값을 반복 할 수 있습니까? 아니면 Enum.GetValues를 사용하여 전체 열거 형을 반복하고 어느 것이 설정되어 있는지 확인해야합니까?
답변
static IEnumerable<Enum> GetFlags(Enum input)
{
foreach (Enum value in Enum.GetValues(input.GetType()))
if (input.HasFlag(value))
yield return value;
}
답변
다음은 문제에 대한 Linq 솔루션입니다.
public static IEnumerable<Enum> GetFlags(this Enum e)
{
return Enum.GetValues(e.GetType()).Cast<Enum>().Where(e.HasFlag);
}
답변
내가 아는 한 각 구성 요소를 가져 오는 기본 제공 방법은 없습니다. 그러나 다음과 같은 방법으로 얻을 수 있습니다.
[Flags]
enum Items
{
None = 0x0,
Foo = 0x1,
Bar = 0x2,
Baz = 0x4,
Boo = 0x6,
}
var value = Items.Foo | Items.Bar;
var values = value.ToString()
.Split(new[] { ", " }, StringSplitOptions.None)
.Select(v => (Items)Enum.Parse(typeof(Items), v));
// This method will always end up with the most applicable values
value = Items.Bar | Items.Baz;
values = value.ToString()
.Split(new[] { ", " }, StringSplitOptions.None)
.Select(v => (Items)Enum.Parse(typeof(Items), v)); // Boo
Enum
내부적으로 문자열을 생성하여 대신 플래그를 반환 하도록 조정했습니다 . 리플렉터에서 코드를 볼 수 있으며 다소 동등해야합니다. 여러 비트를 포함하는 값이있는 일반적인 사용 사례에 적합합니다.
static class EnumExtensions
{
public static IEnumerable<Enum> GetFlags(this Enum value)
{
return GetFlags(value, Enum.GetValues(value.GetType()).Cast<Enum>().ToArray());
}
public static IEnumerable<Enum> GetIndividualFlags(this Enum value)
{
return GetFlags(value, GetFlagValues(value.GetType()).ToArray());
}
private static IEnumerable<Enum> GetFlags(Enum value, Enum[] values)
{
ulong bits = Convert.ToUInt64(value);
List<Enum> results = new List<Enum>();
for (int i = values.Length - 1; i >= 0; i--)
{
ulong mask = Convert.ToUInt64(values[i]);
if (i == 0 && mask == 0L)
break;
if ((bits & mask) == mask)
{
results.Add(values[i]);
bits -= mask;
}
}
if (bits != 0L)
return Enumerable.Empty<Enum>();
if (Convert.ToUInt64(value) != 0L)
return results.Reverse<Enum>();
if (bits == Convert.ToUInt64(value) && values.Length > 0 && Convert.ToUInt64(values[0]) == 0L)
return values.Take(1);
return Enumerable.Empty<Enum>();
}
private static IEnumerable<Enum> GetFlagValues(Type enumType)
{
ulong flag = 0x1;
foreach (var value in Enum.GetValues(enumType).Cast<Enum>())
{
ulong bits = Convert.ToUInt64(value);
if (bits == 0L)
//yield return value;
continue; // skip the zero value
while (flag < bits) flag <<= 1;
if (flag == bits)
yield return value;
}
}
}
확장 방법 GetIndividualFlags()
는 유형에 대한 모든 개별 플래그를 가져옵니다. 따라서 여러 비트를 포함하는 값은 제외됩니다.
var value = Items.Bar | Items.Baz;
value.GetFlags(); // Boo
value.GetIndividualFlags(); // Bar, Baz
답변
몇 년 후 조금 더 많은 경험을 바탕으로 다시 돌아와서, 단일 비트 값에 대한 나의 궁극적 인 대답은 가장 낮은 비트에서 가장 높은 비트로 이동하는 Jeff Mercado의 내부 루틴의 약간의 변형입니다.
public static IEnumerable<Enum> GetUniqueFlags(this Enum flags)
{
ulong flag = 1;
foreach (var value in Enum.GetValues(flags.GetType()).Cast<Enum>())
{
ulong bits = Convert.ToUInt64(value);
while (flag < bits)
{
flag <<= 1;
}
if (flag == bits && flags.HasFlag(value))
{
yield return value;
}
}
}
그것은 효과가있는 것으로 보이며 몇 년 전의 이의 제기에도 불구하고 HasFlag를 사용합니다. 비트 비교를 사용하는 것보다 훨씬 읽기 쉽고 속도 차이는 내가 할 일에 중요하지 않기 때문입니다. (그들은 어쨌든 HasFlags의 속도를 향상 시켰을 가능성이 있습니다. 아무것도 알지 못했지만 테스트하지 않았습니다.)
답변
@Greg의 메소드에서 벗어나고 C # 7.3의 새로운 기능을 추가하면 다음과 같은 Enum
제약이 있습니다.
public static IEnumerable<T> GetUniqueFlags<T>(this Enum flags)
where T : Enum // New constraint for C# 7.3
{
foreach (Enum value in Enum.GetValues(flags.GetType()))
if (flags.HasFlag(value))
yield return (T)value;
}
새로운 제약 조건을 사용하면을 통해 캐스트 할 필요없이 확장 방법이 될 수 있으며 메소드 (int)(object)e
를 사용하여 HasFlag
직접 캐스트 할 수 T
있습니다.value
.
C # 7.3은 지연 및에 대한 제약 조건도 추가했습니다 unmanaged
.
답변
@ RobinHood70에서 제공 한 답변에 +1 나는 그 방법의 일반적인 버전이 나에게 편리하다는 것을 알았다.
public static IEnumerable<T> GetUniqueFlags<T>(this Enum flags)
{
if (!typeof(T).IsEnum)
throw new ArgumentException("The generic type parameter must be an Enum.");
if (flags.GetType() != typeof(T))
throw new ArgumentException("The generic type parameter does not match the target type.");
ulong flag = 1;
foreach (var value in Enum.GetValues(flags.GetType()).Cast<T>())
{
ulong bits = Convert.ToUInt64(value);
while (flag < bits)
{
flag <<= 1;
}
if (flag == bits && flags.HasFlag(value as Enum))
{
yield return value;
}
}
}
C # 7.3을 솔루션 공간으로 가져 오기위한 @AustinWBryan의 편집 및 +1
public static IEnumerable<T> GetUniqueFlags<T>(this T flags) where T : Enum
{
ulong flag = 1;
foreach (var value in Enum.GetValues(flags.GetType()).Cast<T>())
{
ulong bits = Convert.ToUInt64(value);
while (flag < bits)
{
flag <<= 1;
}
if (flag == bits && flags.HasFlag(value as Enum))
{
yield return value;
}
}
}