我正在構建一個函數來擴展Enum.Parse
概念, 安全
因此我寫了如下內容: ide
public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum { if (string.IsNullOrEmpty(value)) return defaultValue; foreach (T item in Enum.GetValues(typeof(T))) { if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item; } return defaultValue; }
我收到了一個錯誤約束,它不能是System.Enum
特殊類。 函數
足夠公平,可是有一種容許通用枚舉的解決方法,仍是我必須模仿Parse
函數並將類型做爲屬性傳遞,這迫使對代碼使用難看的裝箱要求。 this
編輯謝謝全部下面的建議。 spa
已經解決(我離開了循環以保持不區分大小寫-解析XML時正在使用它) code
public static class EnumUtils { public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible { if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type"); if (string.IsNullOrEmpty(value)) return defaultValue; foreach (T item in Enum.GetValues(typeof(T))) { if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item; } return defaultValue; } }
編輯: (2015年2月16日)Julien Lebosquain最近在下面的MSIL或F#中發佈了由編譯器強制執行的類型安全的通用解決方案 ,這很值得一看,並值得一提。 若是解決方案在頁面上冒泡,我將刪除此編輯。 orm
我喜歡Christopher Currens使用IL的解決方案,但對於那些不想將MSIL歸入其構建過程的棘手事務的人,我在C#中編寫了相似的功能。 接口
請注意,儘管不能使用泛型限制,例如where T : Enum
由於Enum是特殊類型。 所以,我必須檢查給定的泛型類型是否真的是枚舉。 事務
個人職能是: ci
public static T GetEnumFromString<T>(string strValue, T defaultValue) { // Check if it realy enum at runtime if (!typeof(T).IsEnum) throw new ArgumentException("Method GetEnumFromString can be used with enums only"); if (!string.IsNullOrEmpty(strValue)) { IEnumerator enumerator = Enum.GetValues(typeof(T)).GetEnumerator(); while (enumerator.MoveNext()) { T temp = (T)enumerator.Current; if (temp.ToString().ToLower().Equals(strValue.Trim().ToLower())) return temp; } } return defaultValue; }
足夠有趣的是,顯然這在其餘語言 (直接由Managed C ++,IL)中也是可能的 。
報價:
...這兩個約束實際上都產生有效的IL,而且若是用另外一種語言編寫,則也能夠由C#使用(您能夠在託管C ++或IL中聲明這些約束)。
誰知道
編輯
Julien Lebosquain如今已經很好地回答了這個問題。 我還想在添加TryParse
和ParseOrDefault
同時,使用ignoreCase
, defaultValue
和可選參數擴展他的答案。
public abstract class ConstrainedEnumParser<TClass> where TClass : class // value type constraint S ("TEnum") depends on reference type T ("TClass") [and on struct] { // internal constructor, to prevent this class from being inherited outside this code internal ConstrainedEnumParser() {} // Parse using pragmatic/adhoc hard cast: // - struct + class = enum // - 'guaranteed' call from derived <System.Enum>-constrained type EnumUtils public static TEnum Parse<TEnum>(string value, bool ignoreCase = false) where TEnum : struct, TClass { return (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase); } public static bool TryParse<TEnum>(string value, out TEnum result, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T { var didParse = Enum.TryParse(value, ignoreCase, out result); if (didParse == false) { result = defaultValue; } return didParse; } public static TEnum ParseOrDefault<TEnum>(string value, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T { if (string.IsNullOrEmpty(value)) { return defaultValue; } TEnum result; if (Enum.TryParse(value, ignoreCase, out result)) { return result; } return defaultValue; } } public class EnumUtils: ConstrainedEnumParser<System.Enum> // reference type constraint to any <System.Enum> { // call to parse will then contain constraint to specific <System.Enum>-class }
用法示例:
WeekDay parsedDayOrArgumentException = EnumUtils.Parse<WeekDay>("monday", ignoreCase:true); WeekDay parsedDayOrDefault; bool didParse = EnumUtils.TryParse<WeekDay>("clubs", out parsedDayOrDefault, ignoreCase:true); parsedDayOrDefault = EnumUtils.ParseOrDefault<WeekDay>("friday", ignoreCase:true, defaultValue:WeekDay.Sunday);
舊
經過使用註釋和「新」開發,我對Vivek答案的舊改進:
TEnum
爲用戶清晰 TryParse
使用現有參數處理ignoreCase
(在VS2010 / .Net 4中引入) default
值 (在VS2005 / .Net 2中引入) defaultValue
和ignoreCase
使用具備默認值的可選參數 (在VS2010 / .Net 4中引入) 致使:
public static class EnumUtils { public static TEnum ParseEnum<TEnum>(this string value, bool ignoreCase = true, TEnum defaultValue = default(TEnum)) where TEnum : struct, IComparable, IFormattable, IConvertible { if ( ! typeof(TEnum).IsEnum) { throw new ArgumentException("TEnum must be an enumerated type"); } if (string.IsNullOrEmpty(value)) { return defaultValue; } TEnum lResult; if (Enum.TryParse(value, ignoreCase, out lResult)) { return lResult; } return defaultValue; } }
我已經將Vivek的解決方案封裝到能夠重用的實用程序類中。 請注意,您仍然應該在類型上定義類型約束「 T:struct,IConvertible」。
using System; internal static class EnumEnforcer { /// <summary> /// Makes sure that generic input parameter is of an enumerated type. /// </summary> /// <typeparam name="T">Type that should be checked.</typeparam> /// <param name="typeParameterName">Name of the type parameter.</param> /// <param name="methodName">Name of the method which accepted the parameter.</param> public static void EnforceIsEnum<T>(string typeParameterName, string methodName) where T : struct, IConvertible { if (!typeof(T).IsEnum) { string message = string.Format( "Generic parameter {0} in {1} method forces an enumerated type. Make sure your type parameter {0} is an enum.", typeParameterName, methodName); throw new ArgumentException(message); } } /// <summary> /// Makes sure that generic input parameter is of an enumerated type. /// </summary> /// <typeparam name="T">Type that should be checked.</typeparam> /// <param name="typeParameterName">Name of the type parameter.</param> /// <param name="methodName">Name of the method which accepted the parameter.</param> /// <param name="inputParameterName">Name of the input parameter of this page.</param> public static void EnforceIsEnum<T>(string typeParameterName, string methodName, string inputParameterName) where T : struct, IConvertible { if (!typeof(T).IsEnum) { string message = string.Format( "Generic parameter {0} in {1} method forces an enumerated type. Make sure your input parameter {2} is of correct type.", typeParameterName, methodName, inputParameterName); throw new ArgumentException(message); } } /// <summary> /// Makes sure that generic input parameter is of an enumerated type. /// </summary> /// <typeparam name="T">Type that should be checked.</typeparam> /// <param name="exceptionMessage">Message to show in case T is not an enum.</param> public static void EnforceIsEnum<T>(string exceptionMessage) where T : struct, IConvertible { if (!typeof(T).IsEnum) { throw new ArgumentException(exceptionMessage); } } }
這是個人見解。 從答案和MSDN結合
public static TEnum ParseToEnum<TEnum>(this string text) where TEnum : struct, IConvertible, IComparable, IFormattable { if (string.IsNullOrEmpty(text) || !typeof(TEnum).IsEnum) throw new ArgumentException("TEnum must be an Enum type"); try { var enumValue = (TEnum)Enum.Parse(typeof(TEnum), text.Trim(), true); return enumValue; } catch (Exception) { throw new ArgumentException(string.Format("{0} is not a member of the {1} enumeration.", text, typeof(TEnum).Name)); } }