考虑到这一点:
[Flags]
public enum MyEnum {
One = 1,
Two = 2,
Four = 4,
Eight = 8
}
public static class FlagsHelper
{
public static bool Contains(this MyEnum keys, MyEnum flag)
{
return (keys & flag) != 0;
}
}
Run Code Online (Sandbox Code Playgroud)
是否有可能编写一个适用于任何产品的通用版本的Contains enum而不仅仅是MyEnum?
编辑:
在阅读完答案后,这将是我的版本:
public static bool Contains(this Enum keys, Enum flag)
{
ulong keysVal = Convert.ToUInt64(keys);
ulong flagVal = Convert.ToUInt64(flag);
return (keysVal & flagVal) == flagVal;
}
Run Code Online (Sandbox Code Playgroud)
刚刚意识到检查我正在检查(return (keys & flag) != 0;)的方式是一个坏主意,因为flag参数实际上可能是几个标志,而常识要做的只有在keys包含所有标志时才返回true .此外,我不会检查空值或甚至确保它们是相同的类型.我可能想要使用不同的类型.
chi*_*emp 54
我将此方法基于一堆SO和Google搜索,并使用反射器来查看MS为.NET 4 HasFlags方法所做的操作.
public static class EnumExt
{
/// <summary>
/// Check to see if a flags enumeration has a specific flag set.
/// </summary>
/// <param name="variable">Flags enumeration to check</param>
/// <param name="value">Flag to check for</param>
/// <returns></returns>
public static bool HasFlag(this Enum variable, Enum value)
{
if (variable == null)
return false;
if (value == null)
throw new ArgumentNullException("value");
// Not as good as the .NET 4 version of this function, but should be good enough
if (!Enum.IsDefined(variable.GetType(), value))
{
throw new ArgumentException(string.Format(
"Enumeration type mismatch. The flag is of type '{0}', was expecting '{1}'.",
value.GetType(), variable.GetType()));
}
ulong num = Convert.ToUInt64(value);
return ((Convert.ToUInt64(variable) & num) == num);
}
}
Run Code Online (Sandbox Code Playgroud)
笔记:
如果将负数定义为标志枚举常量,请谨慎使用,因为许多标志位置可能设置为1,这可能会使您的代码混淆并鼓励编码错误.
这是我的方法,这是类型安全,不做任何装箱或拆箱.如果类型不是枚举,则抛出异常.如果你想将它变成一个将被输入Enum的公共静态方法,你可以使用一种技术,但它不能是一种扩展方法.也没有必要检查null,因为struct contraint也阻止了可以为空的枚举.我不认为要改进这些代码还有很多工作要做,除了可以用F#或C++/CLI编写代码,以便你可以对它进行枚举约束.我的想法是使用表达式树构建一个函数,它将枚举转换为long,如果它只是一个基于ulong的枚举,或ulong然后和它们,基本上生成::
return value & flag == flag
public static class EnumExtensions
{
#region Public Static Methods
/// <summary>
/// Determines whether the specified value has flags. Note this method is up to 60 times faster
/// than the one that comes with .NET 4 as it avoids any explict boxing or unboxing.
/// </summary>
/// <typeparam name="TEnum">The type of the enum.</typeparam>
/// <param name="value">The value.</param>
/// <param name="flag">The flag.</param>
/// <returns>
/// <c>true</c> if the specified value has flags; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentException">If TEnum is not an enum.</exception>
public static bool HasFlags<TEnum>(this TEnum value, TEnum flag) where TEnum:struct,IComparable,IConvertible,IFormattable
{
return EnumExtensionsInternal<TEnum>.HasFlagsDelegate(value, flag);
}
#endregion Public Static Methods
#region Nested Classes
static class EnumExtensionsInternal<TEnum> where TEnum : struct,IComparable, IConvertible, IFormattable
{
#region Public Static Variables
/// <summary>
/// The delegate which determines if a flag is set.
/// </summary>
public static readonly Func<TEnum, TEnum, bool> HasFlagsDelegate = CreateHasFlagDelegate();
#endregion Public Static Variables
#region Private Static Methods
/// <summary>
/// Creates the has flag delegate.
/// </summary>
/// <returns></returns>
private static Func<TEnum, TEnum, bool> CreateHasFlagDelegate()
{
if(!typeof(TEnum).IsEnum)
{
throw new ArgumentException(string.Format("{0} is not an Enum", typeof(TEnum)), typeof(EnumExtensionsInternal<>).GetGenericArguments()[0].Name);
}
ParameterExpression valueExpression = Expression.Parameter(typeof(TEnum));
ParameterExpression flagExpression = Expression.Parameter(typeof(TEnum));
ParameterExpression flagValueVariable = Expression.Variable(Type.GetTypeCode(typeof(TEnum)) == TypeCode.UInt64 ? typeof(ulong) : typeof(long));
Expression<Func<TEnum, TEnum, bool>> lambdaExpression = Expression.Lambda<Func<TEnum, TEnum, bool>>(
Expression.Block(
new[] { flagValueVariable },
Expression.Assign(
flagValueVariable,
Expression.Convert(
flagExpression,
flagValueVariable.Type
)
),
Expression.Equal(
Expression.And(
Expression.Convert(
valueExpression,
flagValueVariable.Type
),
flagValueVariable
),
flagValueVariable
)
),
valueExpression,
flagExpression
);
return lambdaExpression.Compile();
}
#endregion Private Static Methods
}
#endregion Nested Classes
}
Run Code Online (Sandbox Code Playgroud)
由于我忘记了上面的表达式树是.NET 4,因此只有以下方法才能在.NET 3.5中工作以创建相同的表达式树::
private static Func<TEnum, TEnum, bool> CreateHasFlagDelegate2()
{
if(!typeof(TEnum).IsEnum)
{
throw new ArgumentException(string.Format("{0} is not an Enum", typeof(TEnum)), typeof(EnumExtensionsInternal<>).GetGenericArguments()[0].Name);
}
ParameterExpression valueExpression = Expression.Parameter(
typeof(TEnum),
typeof(TEnum).Name
);
ParameterExpression flagExpression = Expression.Parameter(
typeof(TEnum),
typeof(TEnum).Name
);
var targetType = Type.GetTypeCode(typeof(TEnum)) == TypeCode.UInt64 ? typeof(ulong) : typeof(long);
Expression<Func<TEnum, TEnum, bool>> lambdaExpression = Expression.Lambda<Func<TEnum, TEnum, bool>>(
Expression.Equal(
Expression.And(
Expression.Convert(
valueExpression,
targetType
),
Expression.Convert(
flagExpression,
targetType
)
),
Expression.Convert(
flagExpression,
targetType
)
),
valueExpression,
flagExpression
);
return lambdaExpression.Compile();
}
Run Code Online (Sandbox Code Playgroud)
这个版本应该在.NET 3.5中编译,如果不是,我无法理解为什么.
| 归档时间: |
|
| 查看次数: |
23015 次 |
| 最近记录: |