【发布时间】:2014-06-28 18:55:30
【问题描述】:
编辑:
大多数人建议标志枚举应始终具有 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));
}
}
}
}
【问题讨论】:
-
对于正确组合的标志枚举,不同的值都将是 2 的幂。
-
{One=1, Two=2, OneAndTwoMask=3}的情况下 3 的结果应该是什么? -
@PrestonGuillot 我不认为Flags 暗示所有值都是单独的位,而是打算用作位域。 IE。通常有像
ReadOrWrite或掩码PermissionBitsMask这样的“帮助”值 - 所以看看枚举的多位值是否应该被OP视为复合值会很有趣。 -
@AviTurner - 我没有说它是好还是坏的设计 - 只是现有的设计,因此在编写实用程序函数时可能需要考虑它。示例 - FileShare
-
@AlexeiLevenkov “不同”我的意思是“那些不是复合的”,例如
Read和Write,但不是ReadOrWrite。如果 OP 通过“复合”表示 else,我不相信Flags是他正在寻找的东西。
标签: c# .net enums enumeration enum-flags