Печатные флаги Enum как отдельные флаги - PullRequest
9 голосов
/ 04 апреля 2011

У меня есть перечисление флагов, определенное так:

[Flags]
public enum MyEnum
{
    None =     0x00,
    Choice1 =  0x01,
    Choice2 =  0x02,
    Choice3 =  0x04,
    Default =  Choice1 | Choice2,
    All =      Default | Choice3
}

Я бы хотел распечатать, какие флаги включены в MyEnum.Default. В этом случае я бы хотел, чтобы вывод был что-то вроде «Choice1, Choice2».

Проблема с простой печатью MyEnum.Default.ToString() заключается в том, что при выводе «Choice1, Choice2» вывод будет «По умолчанию».

Вот один из вариантов, но если бы я использовал это, мне приходилось обновлять печать каждый раз, когда я менял перечисление.

((StudyData.Choice1 & StudyData.Default) == StudyData.Choice1 ? StudyData.Choice1.ToString() : "") + ", " +
((StudyData.Choice2 & StudyData.Default) == StudyData.Choice2 ? StudyData.Choice2.ToString() : "") + ", " +
((StudyData.Choice3 & StudyData.Default) == StudyData.Choice3 ? StudyData.Choice3.ToString() : "")

У кого-нибудь есть более чистый способ сделать это? В идеале, я бы хотел распечатать флаги, включенные в MyEnum.Default без необходимости изменять код печати каждый раз, когда я добавлял новый флаг или изменял значение по умолчанию.

Спасибо!

Ответы [ 6 ]

14 голосов
/ 04 апреля 2011

Используя методы расширения, которые я написал здесь по связанному вопросу, это должно быть просто:

var value = MyEnum.Default;
var str = String.Join(", ", value.GetIndividualFlags());
// "Choice1, Choice2"

А вот методы расширения:

static class EnumExtensions
{
    public static IEnumerable<Enum> GetFlags(this Enum value)
    {
        return GetFlags(value, Enum.GetValues(value.GetType()).Cast<Enum>().ToArray());
    }

    public static IEnumerable<Enum> GetIndividualFlags(this Enum value)
    {
        return GetFlags(value, GetFlagValues(value.GetType()).ToArray());
    }

    private static IEnumerable<Enum> GetFlags(Enum value, Enum[] values)
    {
        ulong bits = Convert.ToUInt64(value);
        List<Enum> results = new List<Enum>();
        for (int i = values.Length - 1; i >= 0; i--)
        {
            ulong mask = Convert.ToUInt64(values[i]);
            if (i == 0 && mask == 0L)
                break;
            if ((bits & mask) == mask)
            {
                results.Add(values[i]);
                bits -= mask;
            }
        }
        if (bits != 0L)
            return Enumerable.Empty<Enum>();
        if (Convert.ToUInt64(value) != 0L)
            return results.Reverse<Enum>();
        if (bits == Convert.ToUInt64(value) && values.Length > 0 && Convert.ToUInt64(values[0]) == 0L)
            return values.Take(1);
        return Enumerable.Empty<Enum>();
    }

    private static IEnumerable<Enum> GetFlagValues(Type enumType)
    {
        ulong flag = 0x1;
        foreach (var value in Enum.GetValues(enumType).Cast<Enum>())
        {
            ulong bits = Convert.ToUInt64(value);
            if (bits == 0L)
                //yield return value;
                continue; // skip the zero value
            while (flag < bits) flag <<= 1;
            if (flag == bits)
                yield return value;
        }
    }
}
4 голосов
/ 04 апреля 2011

Украсьте ваше перечисление с [FlagsAttribute]. Это делает в точности то, что вы ищете:

[FlagsAttribute]
public enum FooNum
{
    foo = 0,
    bar = 1,
    lulz = 2,
    borkbork = 4
}

FooNum f = FooNum.bar | FooNum.borkbork;

Debug.WriteLine(f.ToString());

должно дать вам:

бар, боркборк

1 голос
/ 08 августа 2017

Печать одним оператором linq:

var names = Enum.GetValues(typeof(MyEnum))
    .Cast<MyEnum>()
    .Where(a => (values & a) == a)
    .Select(a => a.ToString())
    .Aggregate((current, next) => current + ", " + next);

Обновленная версия для печати только явно определенных значений:

var values = MyEnum.All;

var allAttrs = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>();

var names = allAttrs
    // leave only explicitly defined and not zero values
    .Where(attr => allAttrs.Count(a => a != 0 && (attr & a) == a) == 1)   
    .Where(a => (values & a) == a)
    .Select(a=>a.ToString())
    .Aggregate((current, next) => current + ", " + next);

Console.WriteLine(names); // Choice1, Choice2, Choice3
1 голос
/ 05 декабря 2016

Я решил это в кратчайшем и ясном коде, который, как я ожидаю, работает хорошо, хотя в некоторых местах есть бокс.Используя ваш тип в качестве примера:

MyEnum e = MyEnum.Choice1 | MyEnum.Choice2;
string s = FlagsEnumToString<MyEnum>(e); // Returns "Choice1, Choice2"

Вот как это реализовано:

const string Separator = ", ";

public static string FlagsEnumToString<T>(Enum e)
{
    var str = new StringBuilder();

    foreach (object i in Enum.GetValues(typeof(T)))
    {
        if (IsExactlyOneBitSet((int) i) &&
            e.HasFlag((Enum) i))
        {
            str.Append((T) i + Separator);
        }
    }

    if (str.Length > 0)
    {
        str.Length -= Separator.Length;
    }

    return str.ToString();
}

static bool IsExactlyOneBitSet(int i)
{
    return i != 0 && (i & (i - 1)) == 0;
}

Могут появиться некоторые комментарии, и я сначала рассмотрю их:

Мне нужно вызвать ваш метод, предоставив как переменную типа , так и ?

Поскольку это невозможно сделать с помощью общего аргумента T неявно.T нельзя привести к Enum для использования с HasFlag.Нет, также не используется where T : struct, IConvertible.

. foreach также использует object?

Да, также можно использовать заклинание.Только object можно привести к другим типам T, int, Enum.

Я думаю, это можно оптимизировать, приведя к int внутри цикла один раз свременная переменная.

Я так думаю, да.Этот код был написан так для ясности.Так что да, сделайте это и избавьтесь от этих HasFlag вызовов, если хотите.

Я думаю, что вы все еще можете использовать Enum в качестве переменной foreach и сэкономить на приведении.

Нет, потому что вам нужен приведение к T, и это можно сделать только с object.Могут быть и «лучшие» решения, но это, безусловно, самое короткое и ясное решение.

1 голос
/ 28 июня 2012
using System;
using System.Collections.Generic;

using System.Text;

namespace printStar
{
    class Program
    {
        static void Main(string[] args)
        {


            Console.WriteLine("Enter the value ");
            int k = int.Parse(Console.ReadLine());
            int n = k - 1;
            int x = 2 * (k - 1) + 1;

            for (int p = 0; p <= n; p++)
            {
                for (int j = k - 1; j >= 0; j--)
                {
                    Console.Write(" ");
                }

                for (int i = 0; i <= (x - 2 * (k - 1)); i++)
                {
                    if (i % 2 == 1)
                    {
                        Console.Write("*");
                    }
                    else
                    {
                        Console.Write(" ");
                    }
                }

                Console.WriteLine();
                k--;
            }
            Console.ReadLine();
        }
    }
}
0 голосов
/ 16 декабря 2017

Использование flags.ToString("g");
См. Строки формата перечисления

...