2015-01-08 114 views
0

可以按位或枚舉。通常這是在Flags枚舉上完成的。使用泛型的按位或枚舉枚舉

例如var foo = MyEnum.ABC | MyEnum.XCN

我試圖創建一個方法轉換枚舉的陣列成使用泛型組合枚舉。

這是我曾嘗試:

private T CombineFlags<T>(params T[] flags) where T : struct, IConvertible 
{ 
    return flags.Select(flag => flag).Aggregate((x, y) => x | y); 
} 

但是,我不能申請運營商「\」到T和T.鑄造似乎並沒有幫助。 struct, IConvertible似乎是最接近枚舉的,但顯然不夠接近使用'|'運營商。 System.Enum也不是很有幫助。

如何在泛型枚舉上執行此操作? (是否有可能?)

+1

你不能 - 運算符是靜態的,編譯器無法猜測T的傳入 - 它們可能不支持'|'運算符。 –

+1

您可以嘗試的一件事是將值轉換爲「long」,執行操作並將其轉換回來。 –

回答

2

沒有辦法對類型爲Enum的類型應用通用約束,或者應用約束條件使類型重載|運算符,因此您必須執行的任何操作,將無法保持完全靜態類型。

你可以做的是將枚舉改爲它的底層整數類型,進行聚合,然後將其轉換回來。問題是你不能(很容易)解決動態確定底層類型和執行按位或在那種類型(再次由於缺乏對運算符有過載的類型的限制)。如果您可以假設枚舉的基礎類型是int(或任何更小的類型),那麼您可以這樣做,但如果枚舉的基礎類型是long,那麼此代碼將會中斷。還有一個事實是非枚舉值可以用於T,並且這些類型在傳遞給此方法時可能正常工作,也可能無法正常工作。

private static T CombineFlags<T>(params T[] flags) where T : struct, IConvertible 
{ 
    int n = flags.Select(flag => Convert.ToInt32(flag)) 
     .Aggregate((x, y) => x | y); 
    return (T)(object)n; 
} 
+0

長時間可能會更安全。 –

2

您可以創建一個靜態輔助方法來爲您的聚合生成所需的Or函數。該功能在第一次訪問時生成並緩存用於其他用途。

這假定傳入的類型將是一個枚舉。

public T CombineFlags<T>(params T[] flags) 
    where T : struct, IConvertible 
{ 
    return flags.Select(flag => flag).Aggregate(EnumHelper<T>.OrFunction); 
} 

private class EnumHelper<T> 
    where T : struct,IConvertible 
{ 
    static readonly Type typeofT = typeof(T); 
    static readonly Type underlyingType = Enum.GetUnderlyingType(typeofT); 
    static readonly ParameterExpression[] parameters = 
    { 
     Expression.Parameter(typeofT), 
     Expression.Parameter(typeofT) 
    }; 

    static readonly Func<T, T, T> _orFunc = Expression.Lambda<Func<T, T, T>>(
     Expression.Convert(Expression.Or(
      Expression.Convert(parameters[0], underlyingType), 
      Expression.Convert(parameters[1], underlyingType) 
     ), typeofT), parameters).Compile(); 

    public static Func<T, T, T> OrFunction { get { return _orFunc; } } 
}