[英]Printing Flags Enum as Separate Flags
I have a flags enum defined like this: 我有一个像这样定义的标志枚举:
[Flags]
public enum MyEnum
{
None = 0x00,
Choice1 = 0x01,
Choice2 = 0x02,
Choice3 = 0x04,
Default = Choice1 | Choice2,
All = Default | Choice3
}
I would like a way to print out which flags are included in MyEnum.Default
. 我想要一种方法来打印
MyEnum.Default
中包含哪些标志。 In this case, I'd want the output to be something like "Choice1, Choice2". 在这种情况下,我希望输出类似于“Choice1,Choice2”。
The problem with simply printing MyEnum.Default.ToString()
is that the output would be "Default" when I want "Choice1, Choice2". 简单地打印
MyEnum.Default.ToString()
是当我想要“Choice1,Choice2”时输出将是“Default”。
Here's one option, but if I used this I'd have to update the printing every time I changed the enum. 这是一个选项,但如果我使用它,我每次更改枚举时都必须更新打印。
((StudyData.Choice1 & StudyData.Default) == StudyData.Choice1 ? StudyData.Choice1.ToString() : "") + ", " +
((StudyData.Choice2 & StudyData.Default) == StudyData.Choice2 ? StudyData.Choice2.ToString() : "") + ", " +
((StudyData.Choice3 & StudyData.Default) == StudyData.Choice3 ? StudyData.Choice3.ToString() : "")
Does anyone have a cleaner way of doing this? 有没有人有更清洁的方式这样做? Ideally, I'd like a way of printing out the flags included in
MyEnum.Default
without having to change the printing code every time I added a new flag or changed the default. 理想情况下,我想要一种打印
MyEnum.Default
包含的标志的方法,而不必在每次添加新标志或更改默认值时更改打印代码。
Thanks! 谢谢!
Using the extension methods I've written here on a related question, this should be simple: 使用我在这里写的相关问题的扩展方法,这应该很简单:
var value = MyEnum.Default;
var str = String.Join(", ", value.GetIndividualFlags());
// "Choice1, Choice2"
And here's the extension methods: 这是扩展方法:
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;
}
}
}
Decorate your enum with [FlagsAttribute]
. 用
[FlagsAttribute]
装饰你的枚举。 It does pretty much exactly what you're after: 它几乎完全符合您的要求:
[FlagsAttribute]
public enum FooNum
{
foo = 0,
bar = 1,
lulz = 2,
borkbork = 4
}
FooNum f = FooNum.bar | FooNum.borkbork;
Debug.WriteLine(f.ToString());
should give you: 应该给你:
bar, borkbork 酒吧,borkbork
using System;
using System.Collections.Generic;
using System.Text;
namespace printStar
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter the value ");
int k = int.Parse(Console.ReadLine());
int n = k - 1;
int x = 2 * (k - 1) + 1;
for (int p = 0; p <= n; p++)
{
for (int j = k - 1; j >= 0; j--)
{
Console.Write(" ");
}
for (int i = 0; i <= (x - 2 * (k - 1)); i++)
{
if (i % 2 == 1)
{
Console.Write("*");
}
else
{
Console.Write(" ");
}
}
Console.WriteLine();
k--;
}
Console.ReadLine();
}
}
}
I solved this in the shortest, clearest code possible that I expect performs well, although there is boxing in a couple of places. 我用最短,最清晰的代码解决了这个问题,我期望它能很好地运行,尽管在几个地方都有拳击。 Using your type as an example:
以您的类型为例:
MyEnum e = MyEnum.Choice1 | MyEnum.Choice2;
string s = FlagsEnumToString<MyEnum>(e); // Returns "Choice1, Choice2"
This is how it's implemented: 这就是它的实现方式:
const string Separator = ", ";
public static string FlagsEnumToString<T>(Enum e)
{
var str = new StringBuilder();
foreach (object i in Enum.GetValues(typeof(T)))
{
if (IsExactlyOneBitSet((int) i) &&
e.HasFlag((Enum) i))
{
str.Append((T) i + Separator);
}
}
if (str.Length > 0)
{
str.Length -= Separator.Length;
}
return str.ToString();
}
static bool IsExactlyOneBitSet(int i)
{
return i != 0 && (i & (i - 1)) == 0;
}
Some comments might come up and I'll address these first: 可能会提出一些意见,我将首先解决这些问题:
I need to call your method providing both type and variable?
我需要调用你的方法提供类型和变量?
Because this can't be done with a generic T
argument implicitly. 因为这不能隐含地使用泛型
T
参数。 T
can't be cast to Enum
for use with HasFlag
. T
不能转换为Enum
以与HasFlag
一起使用。 No, also not using where T : struct, IConvertible
. 不,也不使用
where T : struct, IConvertible
。
The
foreach
also usesobject
?foreach
还使用object
?
Yes, also to be able to cast. 是的,也是为了能够施展。 Only
object
can be cast to the other types T
, int
, Enum
. 只有
object
可以转换为其他类型T
, int
, Enum
。
I think this can be optimized by casting to
int
inside the loop once with a temporary variable.我认为这可以通过使用临时变量在循环内部转换为
int
来优化。
I think so, yes. 我想是的,是的。 This code was written like this for clarity.
为清楚起见,此代码是这样写的。 So yes do that and get rid of those
HasFlag
calls if you like. 所以是的,如果你愿意,可以摆脱那些
HasFlag
调用。
I think you still can use
Enum
as theforeach
variable and save on casting.我认为你仍然可以使用
Enum
作为foreach
变量并节省转换。
No, because you need a cast to T
and that can only be done from object
. 不,因为你需要一个强制转换为
T
而且只能从object
完成。 There might be 'better' solutions but this is most certainly the shortest and clearest one. 可能有“更好”的解决方案,但这绝对是最短且最清晰的解决方案。
Print by single linq statement: 单个linq声明打印:
var names = Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>()
.Where(a => (values & a) == a)
.Select(a => a.ToString())
.Aggregate((current, next) => current + ", " + next);
Updated version to print only explicitly defined values: 更新版本仅打印明确定义的值:
var values = MyEnum.All;
var allAttrs = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>();
var names = allAttrs
// leave only explicitly defined and not zero values
.Where(attr => allAttrs.Count(a => a != 0 && (attr & a) == a) == 1)
.Where(a => (values & a) == a)
.Select(a=>a.ToString())
.Aggregate((current, next) => current + ", " + next);
Console.WriteLine(names); // Choice1, Choice2, Choice3
Use flags.ToString("g");
使用
flags.ToString("g");
See Enumeration Format Strings 请参阅枚举格式字符串
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.