编辑:
大多数人建议标志枚举应始终具有 2 次方的值。这可能是最佳实践,但我不是在这里定义枚举,而是检查它们并希望在合理范围内涵盖所有可能的场景。问题实际上是关于实现名为EnumUtilities.IsValueDefinedAndComposite<T>
.
原帖:
考虑以下枚举:
[Flags]
public enum TestWithFlags { One = 1, Two = 2, }
以下是将Enum.IsDefined
各种值转换为的结果TestWithFlags
。
输出:
(1). Defined: True: One.
(2). Defined: True: Two.
(3). Defined: False: 100.
(4). Defined: False: One, Two.
(5). Defined: ?????: One, Two.
我无法弄清楚的是如何确定枚举值是复合的。请参阅EnumUtilities.IsValueDefinedAndComposite<T>
下面代码中的功能。
为方便起见,这是完整的代码。
using System;
using System.Collections.Generic;
using System.Linq;
namespace MyNamespace
{
[Flags]
public enum TestWithFlags { One = 1, Two = 2, }
public static class Program
{
private static void Main (string [] args)
{
TestWithFlags value;
value = TestWithFlags.One; // True.
Console.WriteLine("(1). Defined: {0}: {1}.", Enum.IsDefined(typeof(TestWithFlags), value), value.ToString());
value = TestWithFlags.Two; // True.
Console.WriteLine("(2). Defined: {0}: {1}.", Enum.IsDefined(typeof(TestWithFlags), value), value.ToString());
value = (TestWithFlags) 100; // False.
Console.WriteLine("(3). Defined: {0}: {1}.", Enum.IsDefined(typeof(TestWithFlags), value), value.ToString());
value = TestWithFlags.One | TestWithFlags.Two; // False.
Console.WriteLine("(4). Defined: {0}: {1}.", Enum.IsDefined(typeof(TestWithFlags), value), value.ToString());
value = TestWithFlags.One | TestWithFlags.Two; // Not implemented.
Console.WriteLine("(5). Defined: N/A: {1}.", EnumUtilities.IsValueDefinedAndComposite(value), value.ToString());
Console.WriteLine();
Console.Write("Press any key to continue...");
Console.ReadKey(true);
}
}
public static class EnumUtilities
{
public static List<T> GetValues<T> ()
where T: struct, IComparable, IFormattable, IConvertible
{
EnumUtilities.ThrowOnNonEnum<T>();
var list = Enum.GetValues(typeof(T)).OfType<T>().ToList().ConvertAll<T>(v => ((T) v));
return (list);
}
public static bool IsValueDefinedAndComposite<T> (T value)
where T: struct, IComparable, IFormattable, IConvertible
{
EnumUtilities.ThrowOnEnumWithoutFlags<T>();
var values = EnumUtilities.GetValues<T>();
var result = false;
//var result = values.Count(v => (value | v) == value) > 1;
// How to determine whether the argument [value] is composite.
return (result);
}
public static bool IsValueDefinedAndNonComposite<T> (T value)
where T: struct, IComparable, IFormattable, IConvertible
{
EnumUtilities.ThrowOnNonEnum<T>();
return (Enum.IsDefined(typeof(T), value));
}
public static bool IsValueDefined<T> (T value)
where T: struct, IComparable, IFormattable, IConvertible
{
EnumUtilities.ThrowOnNonEnum<T>();
return (EnumUtilities.IsValueDefinedAndNonComposite(value) || EnumUtilities.IsValueDefinedAndComposite(value));
}
private static void ThrowOnNonEnum<T> ()
{
if (!typeof(T).IsEnum)
{
throw (new ArgumentException("The generic argument [<T>] must be an enumeration.", "T: " + typeof(T).FullName));
}
}
private static void ThrowOnEnumWithFlags<T> ()
{
EnumUtilities.ThrowOnNonEnum<T>();
var attributes = typeof(T).GetCustomAttributes(typeof(FlagsAttribute), false);
if (attributes.Length > 0)
{
throw (new ArgumentException("The generic argument [<T>] must be an enumeration without the [FlagsAttribute] applied.", "T: " + typeof(T).FullName));
}
}
private static void ThrowOnEnumWithoutFlags<T> ()
{
EnumUtilities.ThrowOnNonEnum<T>();
var attributes = typeof(T).GetCustomAttributes(typeof(FlagsAttribute), false);
if (attributes.Length == 0)
{
throw (new ArgumentException("The generic argument [<T>] must be an enumeration with the [FlagsAttribute] applied.", "T: " + typeof(T).FullName));
}
}
}
}