我正在构建一个函数来扩展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)); } }