我正在构建一个函数来扩展Enum。解析概念
允许在没有找到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。
很好,但是是否有一种变通方法来允许通用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日)Christopher currents在下面的MSIL或f#中发布了一个编译器强制类型安全的通用解决方案,非常值得一看,并获得好评。如果解决方案在页面上方出现气泡,我将删除此编辑。
编辑2:(2021年4月13日)自从c# 7.3以来,这个问题已经得到解决和支持,我已经更改了公认的答案,尽管出于学术和历史兴趣,充分阅读顶部的答案是值得的:)
如前所述;虽然这不能在源代码中表达,但实际上可以在IL级别上完成。
@Christopher current的回答显示了印度是怎么做的。
与Fodys插件extronstraints。Fody,有一种非常简单的方法,通过构建工具来实现这一点。只需将它们的nuget包(Fody, ExtraConstraints.Fody)添加到您的项目,并添加约束如下(摘录自ExtraConstraints自述):
public void MethodWithEnumConstraint<[EnumConstraint] T>() {...}
public void MethodWithTypeEnumConstraint<[EnumConstraint(typeof(ConsoleColor))] T>() {...}
Fody将为约束添加必要的IL。
还要注意约束委托的附加特性:
public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{...}
public void MethodWithTypeDelegateConstraint<[DelegateConstraint(typeof(Func<int>))] T> ()
{...}
关于枚举,您可能还想注意非常有趣的Enums.NET。
Edit
这个问题现在已经被朱利安·勒博斯昆完美地回答了。
我还想用ignoreCase, defaultValue和可选参数扩展他的答案,同时添加TryParse和ParseOrDefault。
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);
Old
通过使用评论和“新”发展,我对Vivek的答案进行了旧的改进:
为了方便用户使用TEnum
为额外的约束检查添加更多的接口约束
让TryParse用现有参数处理ignoreCase
(在VS2010/中引入。4)净
可选地使用通用缺省值(在VS2005/中引入)。2)净
使用可选参数(在VS2010/中引入)。Net 4)默认值,用于defaultValue和ignoreCase
导致:
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;
}
}
现有的答案是正确的c# <=7.2。然而,有一个c#语言的功能请求(绑定到一个corefx功能请求)允许以下;
public class MyGeneric<TEnum> where TEnum : System.Enum
{ }
在撰写本文时,该特性正在语言开发会议上“讨论中”。
EDIT
根据nawfal的信息,这是在c# 7.3中引入的。
编辑2
现在在c# 7.3版本中(发布说明)
样本;
public static Dictionary<int, string> EnumNamedValues<T>()
where T : System.Enum
{
var result = new Dictionary<int, string>();
var values = Enum.GetValues(typeof(T));
foreach (int item in values)
result.Add(item, Enum.GetName(typeof(T), item));
return result;
}