如何解决我如何获得一个枚举标记的所有可能的组合
|[Flags]
public enum MyEnum
{
None = 0,Setting1 = (1 << 1),Setting2 = (1 << 2),Setting3 = (1 << 3),Setting4 = (1 << 4),}
我需要能够以某种方式遍历每个可能的设置,并将设置组合传递给函数。可悲的是我一直无法弄清楚该怎么做
解决方法
未经测试,使用风险自负,但应一般地解决此问题。
System.Enum
不是有效的限制,因为从技术上讲,C#仅允许在with2ѭ中进行继承或在class
中进行继承,后端绕过Enum
和ValueType
。对不起,我的丑陋铸造。它也不是非常有效,但是除非您针对动态生成的类型运行它,否则每次执行仅应执行一次(如果保存,则必须执行一次)。
public static List<T> GetAllEnums<T>()
where T : struct
// With C# 7.3 where T : Enum works
{
// Unneeded if you add T : Enum
if (typeof(T).BaseType != typeof(Enum)) throw new ArgumentException(\"T must be an Enum type\");
// The return type of Enum.GetValues is Array but it is effectively int[] per docs
// This bit converts to int[]
var values = Enum.GetValues(typeof(T)).Cast<int>().ToArray();
if (!typeof(T).GetCustomAttributes(typeof(FlagsAttribute),false).Any())
{
// We don\'t have flags so just return the result of GetValues
return values;
}
var valuesInverted = values.Select(v => ~v).ToArray();
int max = 0;
for (int i = 0; i < values.Length; i++)
{
max |= values[i];
}
var result = new List<T>();
for (int i = 0; i <= max; i++)
{
int unaccountedBits = i;
for (int j = 0; j < valuesInverted.Length; j++)
{
// This step removes each flag that is set in one of the Enums thus ensuring that an Enum with missing bits won\'t be passed an int that has those bits set
unaccountedBits &= valuesInverted[j];
if (unaccountedBits == 0)
{
result.Add((T)(object)i);
break;
}
}
}
//Check for zero
try
{
if (string.IsNullOrEmpty(Enum.GetName(typeof(T),(T)(object)0)))
{
result.Remove((T)(object)0);
}
}
catch
{
result.Remove((T)(object)0);
}
return result;
}
通过获取所有值并将它们进行“或”运算,而不是求和,以防万一包含复合数字。然后,它将每个整数取到最大,并用每个Flag的相反值对其进行屏蔽,这使有效位变为0,从而使我们能够识别那些不可能的位。
最后的检查是从枚举中缺少零。如果可以,只要在结果中始终包含零枚举,就可以删除它。
给定包含2,4,6,32,34,16384的枚举时,得到15的预期结果。
,这是您的代码示例特有的解决方案,使用简单的for循环(请勿使用,请参见下面的更新)
int max = (int)(MyEnum.Setting1 | MyEnum.Setting2 | MyEnum.Setting3 | MyEnum.Setting4);
for (int i = 0; i <= max; i++)
{
var value = (MyEnum)i;
SomeOtherFunction(value);
}
更新:这是一个通用方法,将返回所有可能的组合。还要感谢@David Yaw提出使用队列来构建每个组合的想法。
IEnumerable<T> AllCombinations<T>() where T : struct
{
// Constuct a function for OR-ing together two enums
Type type = typeof(T);
var param1 = Expression.Parameter(type);
var param2 = Expression.Parameter(type);
var orFunction = Expression.Lambda<Func<T,T,T>>(
Expression.Convert(
Expression.Or(
Expression.Convert(param1,type.GetEnumUnderlyingType()),Expression.Convert(param2,type.GetEnumUnderlyingType())),type),param1,param2).Compile();
var initalValues = (T[])Enum.GetValues(type);
var discoveredCombinations = new HashSet<T>(initalValues);
var queue = new Queue<T>(initalValues);
// Try OR-ing every inital value to each value in the queue
while (queue.Count > 0)
{
T a = queue.Dequeue();
foreach (T b in initalValues)
{
T combo = orFunction(a,b);
if (discoveredCombinations.Add(combo))
queue.Enqueue(combo);
}
}
return discoveredCombinations;
}
, public IEnumerable<TEnum> AllCombinations<TEnum>() where TEnum : struct
{
Type enumType = typeof (TEnum);
if (!enumType.IsEnum)
throw new ArgumentException(string.Format(\"The type {0} does not represent an enumeration.\",enumType),\"TEnum\");
if (enumType.GetCustomAttributes(typeof (FlagsAttribute),true).Length > 0) //Has Flags attribute
{
var allCombinations = new HashSet<TEnum>();
var underlyingType = Enum.GetUnderlyingType(enumType);
if (underlyingType == typeof (sbyte) || underlyingType == typeof (short) || underlyingType == typeof (int) || underlyingType == typeof (long))
{
long[] enumValues = Array.ConvertAll((TEnum[]) Enum.GetValues(enumType),value => Convert.ToInt64(value));
for (int i = 0; i < enumValues.Length; i++)
FillCombinationsRecursive(enumValues[i],i + 1,enumValues,allCombinations);
}
else if (underlyingType == typeof (byte) || underlyingType == typeof (ushort) || underlyingType == typeof (uint) || underlyingType == typeof (ulong))
{
ulong[] enumValues = Array.ConvertAll((TEnum[]) Enum.GetValues(enumType),value => Convert.ToUInt64(value));
for (int i = 0; i < enumValues.Length; i++)
FillCombinationsRecursive(enumValues[i],allCombinations);
}
return allCombinations;
}
//No Flags attribute
return (TEnum[]) Enum.GetValues(enumType);
}
private void FillCombinationsRecursive<TEnum>(long combination,int start,long[] initialValues,HashSet<TEnum> combinations) where TEnum : struct
{
combinations.Add((TEnum)Enum.ToObject(typeof(TEnum),combination));
if (combination == 0)
return;
for (int i = start; i < initialValues.Length; i++)
{
var nextCombination = combination | initialValues[i];
FillCombinationsRecursive(nextCombination,initialValues,combinations);
}
}
private void FillCombinationsRecursive<TEnum>(ulong combination,ulong[] initialValues,combinations);
}
}
,首先,获取所有单个值的列表。由于您有5个值,因此(1 << 5)
= 32个组合,因此从1到31进行迭代。(不要从零开始,这意味着不包含任何枚举值。)进行迭代时,请检查数字中的位,迭代变量中的每一位都意味着要包含该枚举值。将结果放入HashSet中,以确保没有重复项,因为包含\'None \'值不会更改结果枚举。
List<MyEnum> allValues = new List<MyEnum>(Enum.Getvalues(typeof(MyEnum)));
HashSet<MyEnum> allCombos = new Hashset<MyEnum>();
for(int i = 1; i < (1<<allValues.Count); i++)
{
MyEnum working = (MyEnum)0;
int index = 0;
int checker = i;
while(checker != 0)
{
if(checker & 0x01 == 0x01) working |= allValues[index];
checker = checker >> 1;
index++;
}
allCombos.Add(working);
}
,由于它是一个标记的枚举,所以为什么不简单:
获得枚举的最高值。
计算组合的范围,即上限。
迭代每个组合,即从0循环到上限。
一个例子看起来像这样
var highestEnum = Enum.GetValues(typeof(MyEnum)).Cast<int>().Max();
var upperBound = highestEnum * 2;
for (int i = 0; i < upperBound; i++)
{
Console.WriteLine(((MyEnum)i).ToString());
}
,当我向枚举添加新成员时,我通常不希望更新表示枚举最大值的每个变量。
例如,我不喜欢格雷格提出的声明:
int max = (int)(MyEnum.Setting1 | MyEnum.Setting2 | ... | MyEnum.SettingN);
考虑一下当您在整个解决方案中散布着几个可变符时,您决定修改枚举。这肯定不是理想的情况。
我会事先承认我的代码较慢,但是在修改枚举后它会自动正确,并且我会努力以这种健壮的方式进行编码。我愿意为此付出一些计算上的损失,反正C#就是这样。我提议:
public static IEnumerable<T> GetAllValues<T>() where T : struct
{
if (!typeof(T).IsEnum) throw new ArgumentException(\"Generic argument is not an enumeration type\");
int maxEnumValue = (1 << Enum.GetValues(typeof(T)).Length) - 1;
return Enumerable.Range(0,maxEnumValue).Cast<T>();
}
假设枚举包含2的所有幂直到特定幂(包括0)的成员,就像通常使用标志枚举一样。
,我可能要晚一点,我想离开我的解决方案,该解决方案还包括值以及以(\“ V1 | V2 \”,\“ V1 | V2 | V3 \ ”等)。
我采用了上面提出的解决方案的某些方面,因此,感谢所有发布了先前答案的人:D。
注意:仅适用于将Enum设置为以2为基数的组合的情况。
public static Dictionary<int,string> GetCombinations( this Enum enu)
{
var fields = enu.GetType()
.GetFields()
.Where(f => f.Name != \"value__\")
.DistinctBy(f=> Convert.ToInt32(f.GetRawConstantValue()));
var result = fields.ToDictionary(f=>Convert.ToInt32(f.GetRawConstantValue()),f => f.Name);
int max = Enum.GetValues(enu.GetType()).Cast<int>().Max();
int upperBound = max * 2;
for (int i = 0 ; i <= upperBound ; i += 2)
{
string s = Convert.ToString(i,2).PadLeft(Math.Abs(i-max),\'0\');
Boolean[] bits = s.Select(chs => chs == \'1\' ? true : false)
.Reverse()
.ToArray();
if (!result.ContainsKey(i))
{
var newComb = string.Empty;
for (int j = 1; j < bits.Count(); j++)
{
var idx = 1 << j;
if (bits[j] && result.ContainsKey(idx))
{
newComb = newComb + result[idx] + \" | \";
}
}
newComb = newComb.Trim(new char[] { \' \',\'|\' });
if (!result.ContainsValue(newComb) && !string.IsNullOrEmpty(newComb))
{
result.Add(i,newComb);
}
}
}
return result;
}
,此版本的结论:
没有检查:
public IEnumerable<T> AllCombinations<T>() where T : struct
{
var type = typeof(T);
for (var combination = 0; combination < Enum.GetValues(type).Cast<int>().Max()*2; combination++)
{
yield return (T)Enum.ToObject(type,combination);
}
}
经过一些检查:
public IEnumerable<T> AllCombinations<T>() where T : struct
{
var type = typeof(T);
if (!type.IsEnum)
{
throw new ArgumentException($\"Type parameter \'{nameof(T)}\' must be an Enum type.\");
}
for (var combination = 0; combination < Enum.GetValues(type).Cast<int>().Max()*2; combination++)
{
var result = (T)Enum.ToObject(type,combination);
// Optional check for legal combination.
// (and is not necessary if all flag a ascending exponent of 2 like 2,8...
if (result.ToString() == combination.ToString() && combination != 0)
{
continue;
}
yield return result;
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。