2012-12-11 52 views
1

我在類似於以下遍歷具有僅單個比特字段按位枚舉值

[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" 
} 

我以爲我可以遍歷枚舉的所有值,並檢查值是兩個冪。但我無法弄清楚如何獲得Enum參數的基礎價值。

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); 
} 
+0

關於編輯的'IsPowerOfTwo'方法:考慮使用'Convert.ToInt32(e)'。在C#/ .NET中使用泛型枚舉非常難看。 –

+0

難道你不能施展它嗎? http://stackoverflow.com/questions/943398/enums-returning-int-value – Minthos

+0

沒關係,顯然你不能。 – Minthos

回答

0

這裏的另一種方法。我想,你有更多的選擇,更好的:)

public static class EnumHelpers 
{ 
    public static string ToStringExtended<T>(this Enum e) 
    { 
     if (!(e.GetType().GetCustomAttributes(typeof(FlagsAttribute), true).Length > 0)) 
      return e.ToString(); 

     List<string> eNames = new List<string>(); 
     foreach (T fish in Enum.GetValues(typeof(T))) 
     { 
      Enum num = fish as Enum; 
      if (e.HasFlag(num) && Convert.ToInt32(fish) != 0 && Convert.ToInt32(fish) != Convert.ToInt32(e)) 
       eNames.Add(fish.ToString()); 
     } 

     return eNames.Count > 1 ? String.Join("|", eNames.ToArray()) : e.ToString(); 
    } 
} 

的使用率幾乎是相同的東西Fredirk建議:

Colors c = Colors.Purple; 
Console.WriteLine(c.ToStringExtended<Colors>()); 
// Output : Red|Blue 
0

可以使用HasFlag method

.Where(e.HasFlag) 

不過,我認爲你的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); 
+0

感謝,HasFlag似乎工作。現在我只需要驗證當前值是2的冪。 – Osiris

+0

@Osiris是的,但看到我編輯的答案另一種方法。 –

1

可能有這樣做的更好的方法,但是這應該做你在找什麼:

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

它基本上列出了設置標誌的值,除了那些「包含」其他標誌的值。它通過獲取正在測試的值,將其遞減到零並檢查原始值是否將該遞減值設置爲標誌來計算這一點。最後,它將刪除「None」值,除非沒有設置標誌。

使用它,像這樣:

Colors c = Colors.Purple; 
Console.WriteLine(c.AsString()); 
+0

謝謝,但顏色枚舉只是一個例子。我想要一個方法,我可以通過任何標誌枚舉。 – Osiris

+0

@Osiris那裏,更新代碼是更通用的。你可能想要添加一些檢查來驗證它確實是一個標誌枚舉。 –

+0

@Osiris不能放過它,所以我回來了,發現了一個bug並使它更好。現在它應該可以處理大多數情況(還有沒有用Flags屬性裝飾的枚舉)。 –

0

的回答在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; 
    }