我写了这个 EnumHelper 方法
public static IEnumerable<T> AsEnumerable<TEnum, T>(Func<TEnum, T> projection = null) where TEnum : struct
{
if (!typeof(TEnum).IsEnum)
throw new InvalidOperationException("Type parameter TEnum must be an enum");
if (projection == null)
return Enum.GetValues(typeof (TEnum)).OfType<TEnum>();
return Enum.GetValues(typeof (TEnum)).OfType<TEnum>().Select(projection);
}
我在第一次返回时收到编译时错误。这将返回一个IEnumerable<TEnum>
错误 46 无法将类型 System.Collections.Generic.IEnumerable<TEnum>
隐式转换为 System.Collections.Generic.IEnumerable<T>
我对T
没有任何约束,所以T
比TEnum
更通用。在IEnumerable<out T>
T
是可变的,那么为什么我仍然得到错误?
方差仅在两种类型之间存在多态关系时才适用。在您的情况下,TEnum
和 T
不限于相关,因此协方差不适用。
您可以通过将枚举成员直接强制转换为目标类型来轻松解决此问题:
if (projection == null)
return Enum.GetValues(typeof(TEnum)).OfType<T>();
编辑:我建议删除projection
参数,并像这样简单地定义您的方法:
public static IEnumerable<TEnum> AsEnumerable<TEnum>() where TEnum : struct
{
if (!typeof(TEnum).IsEnum)
throw new InvalidOperationException("Type parameter TEnum must be an enum");
return Enum.GetValues(typeof(TEnum)).OfType<TEnum>();
}
如果确实需要执行投影,则可以对返回的序列使用标准 LINQ Select
操作:
var optionsA = AsEnumerable<RegexOptions>();
var optionsB = AsEnumerable<RegexOptions>().Select(o => o.ToString());
这将为您提供与代码几乎相同的简洁性,但省去了维护可选参数的麻烦。
编辑2:如果你真的想为投影定义一个重载,我建议实现其中的所有逻辑,然后使用无参数版本中的标识函数调用它:
public static IEnumerable<TEnum> AsEnumerable<TEnum>() where TEnum : struct
{
return AsEnumerable<TEnum, TEnum>(e => e);
}
public static IEnumerable<TResult> AsEnumerable<TEnum, TResult>(
Func<TEnum, TResult> projection) where TEnum : struct
{
if (!typeof(TEnum).IsEnum)
throw new InvalidOperationException("Type parameter TEnum must be an enum");
return Enum.GetValues(typeof(TEnum)).OfType<TEnum>().Select(projection);
}
示例调用:
var optionsA = AsEnumerable<RegexOptions>();
var optionsB = AsEnumerable<RegexOptions, string>(o => o.ToString());