C# 将标志枚举打印为单独的标志

C# 将标志枚举打印为单独的标志,c#,enums,flags,C#,Enums,Flags,我有一个如下定义的标志枚举: [Flags] public enum MyEnum { None = 0x00, Choice1 = 0x01, Choice2 = 0x02, Choice3 = 0x04, Default = Choice1 | Choice2, All = Default | Choice3 } 我想要一种打印出MyEnum.Default中包含哪些标志的方法。在本例中,我希望输出类似于“Choic

我有一个如下定义的标志枚举:

[Flags]
public enum MyEnum
{
    None =     0x00,
    Choice1 =  0x01,
    Choice2 =  0x02,
    Choice3 =  0x04,
    Default =  Choice1 | Choice2,
    All =      Default | Choice3
}
我想要一种打印出MyEnum.Default中包含哪些标志的方法。在本例中,我希望输出类似于“Choice1,Choice2”

简单打印MyEnum.Default.ToString()的问题是,当我想要“Choice1,Choice2”时,输出将是“Default”

这里有一个选项,但如果我使用它,我必须在每次更改枚举时更新打印

((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() : "")
有人有更干净的方法吗?理想情况下,我想要一种打印出MyEnum.Default中包含的标志的方法,而不必在每次添加新标志或更改默认值时更改打印代码

谢谢

用以下内容装饰您的枚举。它几乎完全符合您的要求:

[Flags]
public enum FooNum
{
    foo = 0,
    bar = 1,
    lulz = 2,
    borkbork = 4
}

FooNum f = FooNum.bar | FooNum.borkbork;

Debug.WriteLine(f.ToString());
应该给你:


bar,BorkWork

使用我在一个相关问题上写的扩展方法,这应该很简单:

var value = MyEnum.Default;
var str = String.Join(", ", value.GetIndividualFlags());
// "Choice1, Choice2"
以下是扩展方法:

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;
        }
    }
}
静态类枚举扩展
{
公共静态IEnumerable GetFlags(此枚举值)
{
返回GetFlags(value,Enum.GetValues(value.GetType()).Cast().ToArray());
}
公共静态IEnumerable GetIndividualFlags(此枚举值)
{
返回GetFlags(value,GetFlagValues(value.GetType()).ToArray());
}
私有静态IEnumerable GetFlags(枚举值,枚举[]值)
{
ulong位=Convert.ToUInt64(值);
列表结果=新列表();
对于(int i=values.Length-1;i>=0;i--)
{
ulong mask=Convert.ToUInt64(值[i]);
如果(i==0&&mask==0L)
打破
if((位和掩码)=掩码)
{
结果。添加(值[i]);
位-=掩码;
}
}
如果(位!=0L)
返回可枚举的.Empty();
如果(转换为ToUInt64(值)!=0L)
返回结果;
如果(位==Convert.ToUInt64(值)&&values.Length>0&&Convert.ToUInt64(值[0])==0L)
返回值。取(1);
返回可枚举的.Empty();
}
私有静态IEnumerable GetFlagValues(类型enumType)
{
ulong标志=0x1;
foreach(Enum.GetValues(enumType.Cast()中的var值)
{
ulong位=Convert.ToUInt64(值);
如果(位==0L)
//收益回报值;
continue;//跳过零值
而(标志<位)标志<代码>使用系统;
使用System.Collections.Generic;
使用系统文本;
名称空间打印星
{
班级计划
{
静态void Main(字符串[]参数)
{
Console.WriteLine(“输入值”);
int k=int.Parse(Console.ReadLine());
int n=k-1;
int x=2*(k-1)+1;
对于(int p=0;p=0;j--)
{
控制台。写(“”);
}

对于(int i=0;i我用最短、最清晰的代码解决了这个问题,我希望代码能够很好地执行,尽管在一些地方存在装箱问题。以您的类型为例:

MyEnum e = MyEnum.Choice1 | MyEnum.Choice2;
string s = FlagsEnumToString<MyEnum>(e); // Returns "Choice1, Choice2"
MyEnum e=MyEnum.Choice1 | MyEnum.Choice2;
字符串s=FlagsEnumToString(e);//返回“Choice1,Choice2”
这就是它的实现方式:

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;
}
const字符串分隔符=“,”;
公共静态字符串FlagsEnumToString(枚举e)
{
var str=新的StringBuilder();
foreach(Enum.GetValues(typeof(T))中的对象i)
{
if(IsExactlyOneBitSet((int)i)&&
e、 HasFlag((枚举)i))
{
str.Append((T)i+分隔符);
}
}
如果(str.Length>0)
{
str.Length-=分隔符.长度;
}
return str.ToString();
}
静态布尔IsExactlyOneBitSet(int i)
{
返回i!=0&(i&(i-1))==0;
}
可能会出现一些评论,我将首先讨论这些:

我需要调用提供类型和变量的方法

因为这不能隐式地使用泛型
t
参数来完成。
t
不能强制转换为
Enum
,以便与
HasFlag
一起使用。不,也不能使用
其中t:struct,IConvertible

foreach
还使用
对象

是的,也可以强制转换。只有
对象
可以强制转换为其他类型
T
int
Enum

我认为这可以通过在循环中使用临时变量强制转换到
int
来优化

我想是的,是的。为了清晰起见,这段代码是这样写的。所以,是的,如果你愿意,可以这样做,并摆脱那些
hassflag
调用

我认为您仍然可以使用
Enum
作为
foreach
变量,并在强制转换时保存


不,因为您需要转换到
T
,并且只能从
对象执行转换。可能有“更好”的解决方案,但这肯定是最短、最清晰的解决方案。

通过单个linq语句打印:

var names = Enum.GetValues(typeof(MyEnum))
    .Cast<MyEnum>()
    .Where(a => (values & a) == a)
    .Select(a => a.ToString())
    .Aggregate((current, next) => current + ", " + next);
var name=Enum.GetValues(typeof(MyEnum))
.Cast()
其中(a=>(值&a)==a)
.Select(a=>a.ToString())
.聚合((当前,下一个)=>当前+“,”+下一个);
仅打印明确定义值的更新版本:

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
var values=MyEnum.All;
var allAttrs=Enum.GetValues(typeof(MyEnum)).Cast();
变量名称=allAttrs
//只保留显式定义的值,而不是零值
其中(attr=>allAttrs.Count(a=>a!=0&&(attr&a)==a)==1)
其中(a=>(值&a)==a)
.Select(a=>a.ToString())
.聚合((当前,下一个)=>当前+“,”+下一个);
Console.WriteLine(名称);//选项1,选项2,选项3
使用
标志。ToString(“g”);


请参见

Oops!我忘了在复制粘贴中包含FlagsAttribute。我将修复原始问题。感谢您的快速响应,并对造成的混乱表示抱歉……哦……我知道您在做什么了
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