创建将T约束为枚举的泛型方法
时间:2020-03-05 18:57:41 来源:igfitidea点击:
我正在构建一个函数来扩展Enum.Parse
概念,
- 如果找不到Enum值,则允许解析默认值
- 不区分大小写
所以我写了以下内容:
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
函数并将类型作为属性传递,这迫使对代码的包装要求严格。
编辑谢谢所有下面的建议。
已经解决(我已经离开循环来保持不区分大小写,我在解析XML时正在使用它)
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或者Fbelow中发布了一个由编译器强制执行的类型安全的通用解决方案,非常值得一看,并提出了建议。如果解决方案在页面上冒泡,我将删除此编辑。
解决方案
回答
希望这会有所帮助:
public static TValue ParseEnum<TValue>(string value, TValue defaultValue) where TValue : struct // enum { try { if (String.IsNullOrEmpty(value)) return defaultValue; return (TValue)Enum.Parse(typeof (TValue), value); } catch(Exception ex) { return defaultValue; } }
回答
我修改了dimarzionist的样本。此版本仅适用于Enums,而不能让结构通过。
public static T ParseEnum<T>(string enumString) where T : struct // enum { if (String.IsNullOrEmpty(enumString) || !typeof(T).IsEnum) throw new Exception("Type given must be an Enum"); try { return (T)Enum.Parse(typeof(T), enumString, true); } catch (Exception ex) { return default(T); } }
回答
我们可以为该类定义一个静态构造函数,该构造函数将检查类型T是否为枚举,如果不是,则抛出异常。这是Jeffery Richter在他的书CLR中通过C#提到的方法。
internal sealed class GenericTypeThatRequiresAnEnum<T> { static GenericTypeThatRequiresAnEnum() { if (!typeof(T).IsEnum) { throw new ArgumentException("T must be an enumerated type"); } } }
然后在parse方法中,我们可以仅使用Enum.Parse(typeof(T),input,true)从字符串转换为枚举。最后一个true参数用于忽略输入的大小写。
回答
由于" Enum"类型实现了" IConvertible"接口,因此更好的实现应该是这样的:
public T GetEnumFromString<T>(string value) where T : struct, IConvertible { if (!typeof(T).IsEnum) { throw new ArgumentException("T must be an enumerated type"); } //... }
这仍将允许传递实现" IConvertible"的值类型。机会虽然很少。
回答
足够有趣的是,显然这在其他语言(直接由Managed C ++,IL)中也是有可能的。
去引用:
... Both constraints actually produce valid IL and can also be consumed by C# if written in another language (you can declare those constraints in managed C++ or in IL).
谁知道
回答
我确实有具体要求,要求将枚举与与枚举值关联的文本一起使用。例如,当我使用枚举指定错误类型时,需要使用它来描述错误详细信息。
public static class XmlEnumExtension { public static string ReadXmlEnumAttribute(this Enum value) { if (value == null) throw new ArgumentNullException("value"); var attribs = (XmlEnumAttribute[]) value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof (XmlEnumAttribute), true); return attribs.Length > 0 ? attribs[0].Name : value.ToString(); } public static T ParseXmlEnumAttribute<T>(this string str) { foreach (T item in Enum.GetValues(typeof(T))) { var attribs = (XmlEnumAttribute[])item.GetType().GetField(item.ToString()).GetCustomAttributes(typeof(XmlEnumAttribute), true); if(attribs.Length > 0 && attribs[0].Name.Equals(str)) return item; } return (T)Enum.Parse(typeof(T), str, true); } } public enum MyEnum { [XmlEnum("First Value")] One, [XmlEnum("Second Value")] Two, Three } static void Main() { // Parsing from XmlEnum attribute var str = "Second Value"; var me = str.ParseXmlEnumAttribute<MyEnum>(); System.Console.WriteLine(me.ReadXmlEnumAttribute()); // Parsing without XmlEnum str = "Three"; me = str.ParseXmlEnumAttribute<MyEnum>(); System.Console.WriteLine(me.ReadXmlEnumAttribute()); me = MyEnum.One; System.Console.WriteLine(me.ReadXmlEnumAttribute()); }
回答
我一直很喜欢这样(我们可以根据需要进行修改):
public static IEnumerable<TEnum> GetEnumValues() { Type enumType = typeof(TEnum); if(!enumType.IsEnum) throw new ArgumentException("Type argument must be Enum type"); Array enumValues = Enum.GetValues(enumType); return enumValues.Cast<TEnum>(); }
回答
我试图对代码进行一些改进:
public T LoadEnum<T>(string value, T defaultValue = default(T)) where T : struct, IComparable, IFormattable, IConvertible { if (Enum.IsDefined(typeof(T), value)) { return (T)Enum.Parse(typeof(T), value, true); } return defaultValue; }