8
[Flags]
public enum MyEnum
{
    None = 0,
    Setting1 = (1 << 1),
    Setting2 = (1 << 2),
    Setting3 = (1 << 3),
    Setting4 = (1 << 4),
}

可能なすべての設定をなんとかしてループし、設定の組み合わせを関数に渡すことができる必要があります。悲しいことに、私はこれを行う方法を理解することができませんでした

4

8 に答える 8

7

テストされていません。自己責任で使用してください。ただし、問題は一般的に十分に解決されるはずです。技術的には、C#はバックエンド内/バックエンドでの継承のみを許可するため、これはSystem.Enum有効な制限ではありません。醜いキャスティングでごめんなさい。また、それほど効率的ではありませんが、動的に生成されたタイプに対してこれを実行しない限り、実行ごとに1回(または保存されている場合は1回)実行する必要があります。classEnumValueType

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;
}

これは、合成数が含まれている場合に、合計ではなく、すべての値を取得してそれらをORすることによって機能します。次に、すべての整数を最大値まで取得し、各フラグの逆でそれらをマスクします。これにより、有効なビットが0になり、不可能なビットを識別できるようになります。

最後のチェックは、列挙型からゼロが欠落していないかどうかです。結果に常にゼロの列挙型を含めても問題がない場合は、これを削除できます。

2,4,6,32,34,16384を含む列挙型を指定すると、期待される結果は15になります。

于 2011-05-24T22:58:47.193 に答える
4

フラグが立てられた列挙型なので、単純に:

  1. 列挙型の最高値を取得します。
  2. 組み合わせの量、つまり上限を計算します。
  3. すべての組み合わせを繰り返します。つまり、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());
}
于 2014-11-10T08:24:24.763 に答える
2

単純な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);
}

更新:これは、考えられるすべての組み合わせを返す一般的なメソッドです。そして、キューを使用してすべての組み合わせを構築するというアイデアを提供してくれた@DavidYawに感謝します。

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;
}
于 2011-05-24T21:27:01.890 に答える
2
    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], i + 1, enumValues, 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, i + 1, initialValues, combinations);
        }
    }

    private void FillCombinationsRecursive<TEnum>(ulong combination, int start, ulong[] 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, i + 1, initialValues, combinations);
        }
    }
于 2012-10-24T20:35:46.073 に答える
1

まず、すべての個々の値のリストを取得します。5つの値があるので、つまり(1 << 5)= 32の組み合わせなので、1から31まで繰り返します(ゼロから始めないでください。列挙値が含まれないことを意味します)。繰り返すときは、数値のビットを調べます。反復変数の1ビットごとに、その列挙値を含めることを意味します。'None'値を含めても結果の列挙型は変更されないため、結果をHashSetに入れて、重複がないようにします。

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);
}
于 2011-05-24T21:35:06.240 に答える
0

私は通常、列挙型に新しいメンバーを追加するときに、列挙型の最大値を表す各変数を更新したくありません。たとえば、グレッグが提案した声明は嫌いです。

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を含む)までのメンバーが含まれていることを前提としています。

于 2011-05-24T22:26:04.373 に答える
0

私はおそらくパーティーに少し遅れています。ソリューションには、値と組み合わせの可能なテキスト( "V1 | V2"、 "V1 | V2 | V3"など)も含まれています。

私は上記で提案された解決策のいくつかの側面を取りました、それで以前の答えを投稿したすべてに感謝します:D。

注:列挙型をベース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;
    }
于 2018-08-06T20:16:52.663 に答える
0

このバージョンでの結論:

チェックなし:

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, 4, 8...
        if (result.ToString() == combination.ToString() && combination != 0)
        {
            continue;
        }

        yield return result;
    }
}
于 2018-12-06T14:38:09.843 に答える