C# 循环遍历只包含一个位字段的按位枚举值

C# 循环遍历只包含一个位字段的按位枚举值,c#,reflection,enums,C#,Reflection,Enums,我在代码中定义了多个标志枚举,如下所示 [Flags] public enum Colors { None = 0, Red = 1, Green = 2, Blue = 4, Purple = Red | Blue, Brown = Red | Green, } 以下代码生成以下输出 Colors color1 = Colors.Red | Colors.Blue; Colors color2 = Colors.Purple; string s1 = c

我在代码中定义了多个标志枚举,如下所示

[Flags]
public enum Colors
{
   None = 0,
   Red = 1,
   Green = 2,
   Blue = 4, 
   Purple = Red | Blue,
   Brown = Red | Green,
}
以下代码生成以下输出

Colors color1 = Colors.Red | Colors.Blue;
Colors color2 = Colors.Purple;
string s1 = color1.ToString(); // Sets s1 to "Purple"
string s2 = color2.ToString(); // Sets s2 to "Purple"


我想要一个输出逐位枚举的各个位的方法,即使定义了匹配的组合

private void Foo()
{
  Colors color1 = Colors.Red | Colors.Blue;
  Colors color2 = Colors.Purple;
  string s1 = CreateColumnString(color1); // Sets s1 to "Red|Blue"
  string s2 = CreateColumnString(color2); // Sets s2 to "Red|Blue"
}
我想我可以循环遍历枚举的所有值,并检查该值是否为2的幂。但我不知道如何获取枚举参数的基础值

private string CreateColumnString(object value)
{
 //is this an enum with Flags attribute?
 if (value is Enum  && value.GetType().GetCustomAttributes(typeof(FlagsAttribute), true).Length > 0)
 {
    Enum e = (Enum)value;
    //Get a list of Enum values set in this flags enum
    IEnumerable<Enum> setValues = 
      Enum.GetValues(value.GetType())
          .Cast<Enum>()
          .Where(eachEnum => IsPowerOfTwo(eachEnum) && value.HasFlag(eachEnum)); 

    return string.Join("|", setValues);
 }
 else
 {
    return value != null ? value.ToString() : string.Empty;
 }
 return str;
}

private static bool IsPowerOfTwo(Enum e)
{
   int x = (int)e; //ERROR cannot convert type 'System.Enum' to 'ulong'
   return (x != 0) && ((x & (x - 1)) == 0);
}
私有字符串CreateColumnString(对象值)
{
//这是带有标志属性的枚举吗?
if(值为Enum&&value.GetType().GetCustomAttributes(typeof(FlagsAttribute),true)。长度>0)
{
枚举e=(枚举)值;
//获取在此标志枚举中设置的枚举值列表
IEnumerable集合值=
Enum.GetValues(value.GetType())
.Cast()
其中(eachEnum=>IsPowerOfTwo(eachEnum)和&value.hasvag(eachEnum));
返回string.Join(“|”,setValues);
}
其他的
{
返回值!=null?value.ToString():string.Empty;
}
返回str;
}
私有静态bool IsPowerOfTwo(枚举e)
{
int x=(int)e;//错误无法将类型“System.Enum”转换为“ulong”
返回(x!=0)&((x&(x-1))==0);
}
您可以使用:

但是,我认为您的
Enum.GetValues
调用也将获得枚举类型所命名的多位值

编辑:

下面是另一种方法,您可以开始工作:

if (Enum.GetUnderlyingType(e.GetType()) != typeof(int))
  throw new NotImplementedException();

var list = new List<Enum>();
for (int i = 1; i != 0; i <<= 1)
{
  var eachEnum = (Enum)(Enum.ToObject(e.GetType(), i));
  if (e.HasFlag(eachEnum))
    list.Add(eachEnum);
}

return string.Join(" | ", list);
if(Enum.getUnderlineType(e.GetType())!=typeof(int))
抛出新的NotImplementedException();
var list=新列表();
对于(int i=1;i!=0;i您可以使用:

但是,我认为您的
Enum.GetValues
调用也将获得枚举类型所命名的多位值

编辑:

下面是另一种方法,您可以开始工作:

if (Enum.GetUnderlyingType(e.GetType()) != typeof(int))
  throw new NotImplementedException();

var list = new List<Enum>();
for (int i = 1; i != 0; i <<= 1)
{
  var eachEnum = (Enum)(Enum.ToObject(e.GetType(), i));
  if (e.HasFlag(eachEnum))
    list.Add(eachEnum);
}

return string.Join(" | ", list);
if(Enum.getUnderlineType(e.GetType())!=typeof(int))
抛出新的NotImplementedException();
var list=新列表();

对于(int i=1;i!=0;i可能有更好的方法,但这应该满足您的要求:

private static string AsString<T>(this T values)
{
    Enum v = (Enum)Convert.ChangeType(values, typeof(Enum));
    Array array = Enum.GetValues(typeof(T));
    IEnumerable<Enum> setFlags = array
        .Cast<Enum>()
        .Where(c => v.HasFlag(c) && IsDistinctValue(c));

    return values.Equals(default(T))
        ? default(T).ToString()
        : string.Join("|", setFlags.Where(c => Convert.ToInt32(c) != 0).Select(c => c.ToString()));
}

private static bool IsDistinctValue(Enum value)
{
    int current = Convert.ToInt32(value) >> 1;
    while (current > 0)
    {
        if ((Convert.ToInt32(value) & current) != 0)
        {
            return false;
        }
        current >>= 1;
    }

    return true;
}

也许有更好的方法可以做到这一点,但这应该符合您的要求:

private static string AsString<T>(this T values)
{
    Enum v = (Enum)Convert.ChangeType(values, typeof(Enum));
    Array array = Enum.GetValues(typeof(T));
    IEnumerable<Enum> setFlags = array
        .Cast<Enum>()
        .Where(c => v.HasFlag(c) && IsDistinctValue(c));

    return values.Equals(default(T))
        ? default(T).ToString()
        : string.Join("|", setFlags.Where(c => Convert.ToInt32(c) != 0).Select(c => c.ToString()));
}

private static bool IsDistinctValue(Enum value)
{
    int current = Convert.ToInt32(value) >> 1;
    while (current > 0)
    {
        if ((Convert.ToInt32(value) & current) != 0)
        {
            return false;
        }
        current >>= 1;
    }

    return true;
}

这是另一种方法。我认为你的选择越多越好:)

公共静态类枚举帮助程序
{
公共静态字符串ToString扩展(此枚举e)
{
if(!(e.GetType().GetCustomAttributes(typeof(FlagsAttribute),true).Length>0))
返回e.ToString();
列表名称=新列表();
foreach(T fish在Enum.GetValues(typeof(T))中)
{
Enum num=鱼作为枚举;
如果(e.HasFlag(num)和&Convert.ToInt32(fish)!=0和&Convert.ToInt32(fish)!=Convert.ToInt32(e))
Add(fish.ToString());
}
返回eNames.Count>1?String.Join(“|”,eNames.ToArray()):e.ToString();
}
}
使用方法与Fredirk的建议几乎相同:

Colors c = Colors.Purple;
Console.WriteLine(c.ToStringExtended<Colors>());
// Output : Red|Blue
Colors c=Colors.Purple;
Console.WriteLine(c.ToStringExtended());
//输出:红色|蓝色

这里是另一种方法。我认为你的选择越多越好:)

公共静态类枚举帮助程序
{
公共静态字符串ToString扩展(此枚举e)
{
if(!(e.GetType().GetCustomAttributes(typeof(FlagsAttribute),true).Length>0))
返回e.ToString();
列表名称=新列表();
foreach(T fish在Enum.GetValues(typeof(T))中)
{
Enum num=鱼作为枚举;
如果(e.HasFlag(num)和&Convert.ToInt32(fish)!=0和&Convert.ToInt32(fish)!=Convert.ToInt32(e))
Add(fish.ToString());
}
返回eNames.Count>1?String.Join(“|”,eNames.ToArray()):e.ToString();
}
}
使用方法与Fredirk的建议几乎相同:

Colors c = Colors.Purple;
Console.WriteLine(c.ToStringExtended<Colors>());
// Output : Red|Blue
Colors c=Colors.Purple;
Console.WriteLine(c.ToStringExtended());
//输出:红色|蓝色

不计算方法签名的4行代码中的答案

    private static string CreateColumnString(Colors value)
    {
        // This is how we do it in embedded programming in C
        // In C we wouldn't need to convert, but alas in C# we do
        // So first ... 
        var num = Convert.ToByte(value);  
        // ToUint16 would work as well, but ToByte makes your intentions clearer

        // Then bitwise '& 'every bit position you care about to compose your string
        // For example: 0b0011 & 0b1111 = 0b0011    (3 AND 15 = 3)
        var s = (num & 1) > 0 ? "Red"   : "";
        s = (num & 2) > 0 ? s + "|Green": s;
        return (num & 2) > 0 ? s + "|Blue" : s;
    }

不计算方法签名的4行代码中的答案

    private static string CreateColumnString(Colors value)
    {
        // This is how we do it in embedded programming in C
        // In C we wouldn't need to convert, but alas in C# we do
        // So first ... 
        var num = Convert.ToByte(value);  
        // ToUint16 would work as well, but ToByte makes your intentions clearer

        // Then bitwise '& 'every bit position you care about to compose your string
        // For example: 0b0011 & 0b1111 = 0b0011    (3 AND 15 = 3)
        var s = (num & 1) > 0 ? "Red"   : "";
        s = (num & 2) > 0 ? s + "|Green": s;
        return (num & 2) > 0 ? s + "|Blue" : s;
    }

谢谢,HasFlag似乎起作用了。现在我只需要验证当前值是否为2的幂。谢谢,HasFlag似乎可以工作。现在我只需要验证当前值是2的幂。谢谢,但是颜色枚举只是一个例子。我想要一个可以传递任何标志枚举的方法。@Osiris在那里更新了代码,使之更通用。您可能需要添加一些检查,以验证它是否确实也是一个标志枚举。@Osiris无法释放它,所以我回来了,发现了一个bug并改进了它。现在它应该可以处理大多数场景(也包括没有使用Flags属性修饰的枚举)。我想要一个可以传递任何标志枚举的方法。@Osiris在那里更新了代码,使之更通用。您可能需要添加一些检查,以验证它是否确实也是一个标志枚举。@Osiris无法释放它,所以我回来了,发现了一个bug并改进了它。现在,它应该处理大多数场景(也不是用标志属性修饰的枚举)。关于编辑的<代码> IsPowerOfTwo < /代码>方法:考虑使用<代码>转换。在C#/.NET中,使用泛型枚举非常难看。您不能将其强制转换吗?没关系,显然你不能。关于<代码> IsPowerOfTwo < /Cord>方法的编辑:考虑使用<代码>转换。在C#/.NET中,使用泛型枚举非常难看。您不能将其强制转换吗?没关系,显然你不能。