2011-04-04 58 views
7

我有这样定义的标志枚举:印刷标志枚举为独立的标志

[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”时,输出将是“默认”。

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

((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中的标志,而不必在每次添加新标志或更改默认值时更改打印代码。

谢谢!

回答

10

使用我已经在一个相关的问题书面here扩展方法,这应该是简单:

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; 
     } 
    } 
} 
+1

不错...和无耻地被盗为未来的参考。 – 2011-04-04 19:06:51

+0

请注意,如果您的枚举具有负值(例如使用Visual Studio扩展性框架,Microsoft.VisualStudio.Shell.Interop._VSRDTFLAGS),那么在此使用ulong和ToUInt64会导致问题。如果用long和ToInt64替换,那么它将处理负值的枚举。 – Nerdtron 2017-11-06 15:34:35

3

装饰你的枚举[FlagsAttribute]。它几乎你到底是什么后:

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

FooNum f = FooNum.bar | FooNum.borkbork; 

Debug.WriteLine(f.ToString()); 

应该给你:

酒吧,borkbork

+0

糟糕!我忘了在我的复制粘贴中包含FlagsAttribute。我要解决原来的问题。感谢您的快速反应,并为此感到抱歉... – Brian 2011-04-04 18:48:32

+2

Ohhhh ...我明白你现在在做什么。这个问题即使你用'yourEnum = yourEnum.Choice1 |手动构造'Default' yourEnum.Choice2',当你将它推出到一个字符串时,你仍然会得到'yourEnum.Default'。你需要在枚举本身的默认和所有定义有多严重?你将不得不走路,像你已经发现的那样分开拉动它们。无论如何,这将会有点混乱。另一种选择是将您的默认值存储为他们自己的FooNum。虽然这是自己的方式... – 2011-04-04 19:04:25

1
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(); 
     } 
    } 
} 
1

我用最短,最清晰的代码解决了这个问题,尽管在几个地方有拳击,但我期望表现很好。使用你的类型为例:

MyEnum e = MyEnum.Choice1 | MyEnum.Choice2; 
string s = FlagsEnumToString<MyEnum>(e); // Returns "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; 
} 

有些意见可能遇到的,我会解决这些第一:

我需要调用你的方法提供了类型变量?

因为这不能用隐含的通用T参数来完成。 T不能转换为EnumHasFlag一起使用。不,也没有使用where T : struct, IConvertible

foreach也使用object

是的,也可以施放。只有object可以投射到其他类型T,int,Enum

我认为这可以通过使用临时变量在循环内执行int来优化。

我这么认为,是的。为了清楚起见,此代码是这样写的。所以是的,如果你愿意,可以去掉那些HasFlag电话。

我认为你仍然可以使用Enum作为foreach变量并保存在转换上。

不,因为您需要投射到T,而且只能从object完成。可能会有更好的解决方案,但这绝对是最短和最清晰的解决方案。

1

打印单LINQ声明:

var names = Enum.GetValues(typeof(MyEnum)) 
    .Cast<MyEnum>() 
    .Where(a => (values & a) == a) 
    .Select(a => a.ToString()) 
    .Aggregate((current, next) => current + ", " + next); 

版本更新打印仅明确定义的值:

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