修改
大多数人认为标志枚举应始终具有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));
}
}
}
}
答案 0 :(得分:4)
您可以尝试类似(未经测试!):
public static bool IsValueDefinedAndComposite<T>(T value)
where T : struct, IComparable, IFormattable, IConvertible
{
EnumUtilities.ThrowOnEnumWithoutFlags<T>();
var values = EnumUtilities.GetValues<T>();
var result = values.OfType<T>().Contains(value);
//var result = values.Count(v => (value | v) == value) > 1;
// How to determine whether the argument [value] is composite.
return (result);
}
基本上,只是检查value参数是否是值的一部分,如果不是,则它是复合。
答案 1 :(得分:2)
回答你的问题;您可以通过检查是否是2的幂来检查标志值是单数值还是多个标志的组合。
请参阅How to check if a number is a power of 2
bool IsPowerOfTwo(ulong x)
{
return (x != 0) && ((x & (x - 1)) == 0);
}
如果它不是,那么它有许多设置标志(因为每个标志必须是2的幂)。
答案 2 :(得分:1)
请注意,如果有问题的枚举是严格的位标志{One = 1<<0, Two = 1<< 1, ALot=1<<20}
,那么检查“单位设置”的其他答案会更合适。如果你的枚举可以包含多个位的掩码,请查看这个。即一些虚构的自定义“浮动”数字掩码显示为标志枚举{ Sign = 0x80, Mantissa=0x78, Power = 0x7}
。
测试值是否可以通过枚举中的某些值组合来表示:
一次性:只需开始为每个值删除一位,直到您运行值或结果为0。伪代码(重要的部分是& ~enumValue
- 带有否定值的AND)
var remainingBits = value;
foreach (var enumValue in GetAllValuesOfEnum(....))
{
if (value == enumValue) return "ExisitngNonComposite";
var remainingBits = current & ~enumValue;
if (remainingBits == 0) return "Composite";
}
return "CanNotBeRepresented";
如果您需要重复多次,只关心是否可以表示值:
Flags
枚举,int
/ long
枚举值相应的值不会超过32/64),包括标志的常见组合,如<) LI>
0xFFFFFFFF
) - 可以表示任何值,否则(value & ~ allFilgesOrTogether) == 0
会给你答案。答案 3 :(得分:1)
如果你以稍微不同的方式处理它,这实际上很容易解决:使用F enum string format将枚举转换为字符串,然后检查结果字符串是否包含逗号。
来自Microsoft文档:
如果可能,将枚举条目显示为字符串值。如果 value可以完全显示为。中条目的总和 枚举(即使Flags属性不存在),字符串 每个有效条目的值连接在一起,用\ n分隔 逗号。如果该值不能完全确定 枚举条目,然后将值格式化为整数值。 以下示例说明了F格式说明符。
这将适用于所有枚举,无论它们是否定义了flags属性,因此在下面的代码更新中,我已将初始值测试更改为ThrowOnNonEnum。
以下是使用此方法实现您的方法:
public static bool IsValueDefinedAndComposite<T>(T value)
where T : struct, IComparable, IFormattable, IConvertible
{
EnumUtilities.ThrowOnNonEnum<T>();
var valueAsString = Enum.Format(typeof (T), value, "F");
// If the value contains a comma, then it is defined and composite
if (valueAsString.Contains(","))
{
return true;
}
else
{
// If the value cannot be completely determined by the enumeration entries, it will be numeric.
// This is one possible method for testing this.
double valueAsDouble = 0;
return !(Double.TryParse(valueAsString, out valueAsDouble));
}
}
这是测试项目5的更新版本以及验证未完全定义的场景的新项目6:
value = TestWithFlags.One | TestWithFlags.Two; // True
Console.WriteLine("(5). Defined: {0}: {1}.", EnumUtilities.IsValueDefinedAndComposite(value), value.ToString());
value = (TestWithFlags)6; // False
Console.WriteLine("(6). Defined: {0}: {1}.", EnumUtilities.IsValueDefinedAndComposite(value), value.ToString());
输出:
(1). Defined: True: One.
(2). Defined: True: Two.
(3). Defined: False: 100.
(4). Defined: False: One, Two.
(5). Defined: True: One, Two.
(6). Defined: False: 6.
答案 4 :(得分:0)
您应该将枚举值设置为2的幂。
您可以通过执行(TestWithFlags) 2^NUMBER
测试数字是否为枚举标记来测试它。
答案 5 :(得分:0)
如果我理解你的问题,那么你需要检查复合(flags)枚举是否包含特定值?您是否尝试使用按位操作?
if( 0 != (myCompositeEnum & TestWithFlags.One) )
{
// then TestWithFalgs.One is in the composite.
}
顺便说一句,你的Enum值应该是2的幂
这是StackOverFlow post on a similar question。
答案 6 :(得分:0)
情况可能稍微复杂一些,只是测试传入的值不是2的单个幂,因为定义的枚举标志值本身可以是其他定义的枚举值的组合,例如:
[Flags]
public enum TestWithFlags
{
One = 1,
Two = 2,
Four = 4,
Seven = Four | Two | One,
Eight = 8,
Nine = Eight | One,
}
那么,“复合”在这种情况下意味着什么?它是否意味着“两个或更多有效枚举值的掩码”,或者它是否意味着“不是定义的枚举值而是而是是两个或更多定义的枚举值的掩码”?
假设你想要“不是一个定义的枚举值,而是而是一个两个或多个定义的枚举值的掩码”,以下内容应该有效:
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);
}
private static ulong[] GetValuesAsUint64<T>()
where T : struct, IComparable, IFormattable, IConvertible
{
EnumUtilities.ThrowOnNonEnum<T>();
IList eList = Enum.GetValues(typeof(T));
ulong[] list = new ulong[eList.Count];
for (int i = 0; i < eList.Count; i++)
{
list[i] = Convert.ToUInt64(eList[i]);
}
return list;
}
public static bool IsValueDefinedOrComposite<T>(T value)
where T : struct, IComparable, IFormattable, IConvertible
{
EnumUtilities.ThrowOnEnumWithoutFlags<T>();
var intValue = Convert.ToUInt64(value);
var intValues = GetValuesAsUint64<T>();
if (intValue == 0)
{
return intValues.Contains(intValue);
}
else
{
int matches = 0;
foreach (var test in intValues)
{
if ((test & intValue) == test)
{
matches++;
intValue &= ~(test);
}
}
return matches > 0 && intValue == 0;
}
}
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 IsValueDefinedAndComposite<T>(T value)
where T : struct, IComparable, IFormattable, IConvertible
{
return IsValueDefinedOrComposite(value) && !IsValueDefinedAndNonComposite(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));
}
}
}
请注意,枚举的范围可以从字节大小到(u)长大;每个都应该检查(我没有 - 我只做了一些简单的测试。)。我觉得应该有一个更好的方法来做到这一点,但是enum实用程序自c#1.0以来没有改变,所以非常原始。零值的标志也需要进行测试。
最后,我发现将通用枚举转换为支持位掩码的类型很奇怪;我使用了this suggestion。