[小技巧].Net Framework 4.0 Enum新增的HasFlag方法

我想很多人會習慣用Enum作為旗標,因為同樣是以邏輯And與邏輯Or運算,會比用Int來的好閱讀,並竟誰會知道1、2、4代表什麼東西,尤其專案更多旗標時更難分辦,用Enum因為可以具名化,以文字來代替數字,很容易就可以了解數字代表的意義,不過在.Net Frameword 4.0之前要對Enum作運算,寫的比較多,較為麻煩,也曾寫過ExtensionMethod來簡化作業,不過.Net Frameword 4.0有內建的,就用內建的吧。

我想很多人會習慣用Enum作為旗標,因為同樣是以邏輯And與邏輯Or運算,會比用Int來的好閱讀,並竟誰會知道1、2、4代表什麼東西,尤其專案更多旗標時更難分辦,用Enum因為可以具名化,以文字來代替數字,很容易就可以了解數字代表的意義,不過在.Net Frameword 4.0之前要對Enum作運算,寫的比較多,較為麻煩,也曾寫過ExtensionMethod來簡化作業,不過.Net Framework 4.0有內建的,就用內建的吧。

 

範例

如果用Enum旗標,可以很簡單用一個變數記錄,星期的選擇情況

image

[Flags]
public enum Weekly
{
    None = 0,
    Sunday = 1,
    Monday = 2,
    Tuesday = 4,
    Wednesday = 8,
    Thursday = 16,
    Friday = 32,
    Saturday = 64
}
var selected = Weekly.Sunday | Weekly.Monday | Weekly.Tuesday;

//要知道selected有沒有包含Monday
//方法1 用== Weekly.Monday也可以
var result = (selected & Weekly.Monday).Equals(Weekly.Monday);

//方法2 自已擴充的ExtenstionMethod
result = selected.Has(Weekly.Monday);

//方法3 .Net Framework 4.0後內建的
result = selected.HasFlag(Weekly.Monday);
/// <summary>
/// 自訂的ExtensionMethod,簡化Enum的比較
/// </summary>
public static bool Has(this Enum source, Enum target)
{
    ulong value = Convert.ToUInt64(target);
    return (Convert.ToUInt64(source) & value) == value;
}

 

效能測試

跑100000次比較,所消耗的時間。

image

結果

用Enum會比用Int多耗一些效能,用Method因為多了轉型,比寫死在Code多耗一些效能。

 

測試Code

[TestMethod()]
public void 純Int()
{
    var flags = 1 | 2;
    bool result;
    for (int i = 0; i < 100000; i++)
    {
        result = (flags & 2) == 2;
    }
}

[TestMethod()]
public void 方法1()
{
    var flags = BindingFlags.CreateInstance | BindingFlags.GetField;
    bool result;
    for (int i = 0; i < 100000; i++)
    {
        result = (flags & BindingFlags.GetField) == BindingFlags.GetField;
    }
}

[TestMethod()]
public void 方法2()
{
    var flags = BindingFlags.CreateInstance | BindingFlags.GetField;
    bool result;
    for (int i = 0; i < 100000; i++)
    {
        result = flags.Has(BindingFlags.GetField);
    }
}

[TestMethod()]
public void 方法3()
{
    var flags = BindingFlags.CreateInstance | BindingFlags.GetField;
    bool result;
    for (int i = 0; i < 100000; i++)
    {
        result = flags.HasFlag(BindingFlags.GetField);
    }
}

NOTE:

1.Enum有沒有加[FlagsAttribute]是有差異的,可以看參考資料2的範例,ToString的結果會不同。

2.邏輯運算有And、OR、XOR、NOT等等,請參考資料3、4

 

參考資料

  1. Enum.HasFlag 方法

  2. FlagsAttribute 類別

  3. 邏輯運算符

  4. 位元運算符號