2011-04-04 61 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