[英]How should I represent hierarchical flag enums?
我有以下一組枚舉:
[Flags]
public enum Categories : uint
{
A = (1 << 0),
B = (1 << 1),
B1 = B | (1 << 16),
B2 = B | (1 << 17),
B3 = B | (1 << 18),
B4 = B | (1 << 19),
B5 = B | (1 << 20),
C = (1 << 2),
C1 = C | (1 << 21),
D = (1 << 3),
D1 = D | (1 << 22),
D2 = D | (1 << 23),
E = (1 << 4),
F = (1 << 5),
F1 = F | (1 << 23),
F2 = F | (1 << 24),
F3 = F | (1 << 25),
F4 = F | (1 << 26),
F5 = F | (1 << 27),
G = (1 << 6),
H = (1 << 7),
H1 = H | (1 << 28),
}
這個想法是枚舉表示一個層次結構,其中子枚舉意味着它的父節點,並且可以應用任意數量的標志。
我看到的問題是所有子枚舉都沒有在調試過程中表示為名稱或名稱集。 IE, Categories.F
=“F”但是Categories.F2
= 16777248.我希望Categories.F2
=“F,F2”或至少“F2”
如何讓我的枚舉仍被識別為標志? 有沒有更好的方法來完成我想要做的事情?
調試器中的值與ToString
值不同,這很奇怪。 根據文檔 ,兩者應該匹配(因為Enum
類型確實覆蓋了ToString
)。
如果C#對象具有重寫的
ToString()
,則調試器將調用覆蓋並顯示其結果而不是標准{<typeName>}
。
顯然這不適用於枚舉。 我最好的猜測是調試器正在嘗試對枚舉類型進行一些特殊的,未記錄的處理。 添加DebuggerDisplayAttribute
顯然可以通過覆蓋此行為來解決問題。
[DebuggerDisplay("{ToString()}")]
[Flags]
public enum Categories : uint
{
...
}
Categories.F2.ToString()=“F,F2”
C#不會為你做那個魔術,因為F2
已經在枚舉中擁有它自己的名字。 您可以手動標記各個成員,如下所示:
public enum Categories
{
[Description("F, F2")]
F2 = F | (1 << 24),
}
然后編寫代碼轉換為描述。
public static string ToDescription(this Categories c)
{
var field = typeof(Categories).GetField(c.ToString());
if (field != null)
{
return field.GetCustomAttributes().Cast<DescriptionAttribute>().First().Description;
}
}
...
Categories.F2.ToDescription() == "F, F2";
或者你可以做一些魔術來自己生成:
public static string ToDescription(this Categories c)
{
var categoryNames =
from v in Enum.GetValues(typeof(Categories)).Cast<Category>()
where v & c == c
orderby v
select v.ToString();
return String.Join(", ", categoryNames);
}
遺憾的是,擴展方法不能與DebuggerDisplayAttribute
一起使用,但您可以使用DebuggerTypeAttribute
,YMMV,但您可以嘗試這樣做:
[DebuggerType("CategoryDebugView")]
[Flags]
public enum Categories : uint
{
...
}
internal class CategoryDebugView
{
private Category value;
public CategoryDebugView(Category value)
{
this.value = value;
}
public override string ToString()
{
var categoryNames =
from v in Enum.GetValues(typeof(Categories)).Cast<Category>()
where v & c == c
orderby v
select v.ToString();
return String.Join(", ", categoryNames);
}
}
你可以用一點點工作做你想做的事。 我在Categories
上創建了一些擴展方法,這些方法使用HasFlag()
來確定枚舉值是否具有特定父級,然后對它們調用ToString()
並連接結果。
public static class CategoriesExtensionMethods
{
public static Categories GetParentCategory(this Categories category)
{
Categories[] parents =
{
Categories.A,
Categories.B,
Categories.C,
Categories.D,
Categories.E,
Categories.F,
Categories.G,
Categories.H,
};
Categories? parent = parents.SingleOrDefault(e => category.HasFlag(e));
if (parent != null)
return (Categories)parent;
return Categories.None;
}
public static string ToStringWithParent(this Categories category)
{
var parent = GetParentCategory(category);
if (parent == Categories.None)
return category.ToString();
return string.Format("{0} | {1}", parent.ToString(), category.ToString());
}
}
然后我們可以像這樣使用它:
var f1 = Categories.F1;
var f1ParentString = f1.ToStringWithParent();
// f1ParentString = "F | F1"
var f = Categories.F;
var fParentString = f.GetParentCategory();
// fParentString = "F"
如果你不想指定你的所有父母,這是一個更好的方法來實現GetParentCategory()
。
public static Categories GetParentCategory(this Categories category)
{
var values = Enum.GetValues(typeof(Categories)).Cast<Categories>();
var parent = values.SingleOrDefault(e => category.HasFlag(e) && e != Categories.None && category != e);
if (parent != Categories.None)
return (Categories)parent;
return Categories.None;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.