我正在构建一个函数来扩展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。
还应该考虑到,由于使用Enum约束的c# 7.3版本是开箱即用的,无需做额外的检查和其他工作。
因此,如果你已经将项目的语言版本更改为c# 7.3,下面的代码将会完美地工作:
private static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
// Your code goes here...
}
如果你不知道如何将语言版本更改为c# 7.3,请参阅下面的截图:
编辑1 -需要的Visual Studio版本,并考虑ReSharper
要让Visual Studio能够识别新的语法,您至少需要版本15.7。你可以在微软的发布说明中找到,参见Visual Studio 2017 15.7发布说明。谢谢@ mohammedelshawaf指出了这个有效的问题。
请注意,在我写这篇文章时,ReSharper 2018.1还不支持c# 7.3。ReSharper激活它突出显示Enum约束作为一个错误告诉我不能使用“系统”。数组”、“系统。委托”、“系统。枚举”、“系统。ValueType', 'object'作为类型参数约束。
ReSharper建议作为一个快速修复删除'Enum'约束类型参数T的方法
然而,如果你在Tools -> Options -> ReSharper Ultimate -> General下暂时关闭ReSharper,你会发现如果你使用VS 15.7或更高版本和c# 7.3或更高版本,语法是完美的。
我将Vivek的解决方案封装到一个可以重用的实用程序类中。请注意,你仍然应该在你的类型上定义类型约束“where 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);
}
}
}