将标志枚举打印为单独的标志

发布于 2024-10-30 07:12:50 字数 909 浏览 2 评论 0原文

我有一个如下定义的标志枚举:

[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”时,输出将是“Default”。

这是一个选项,但如果我使用它,我每次更改枚举时都必须更新打印。

((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 中包含的标志的方法,而不必每次添加新标志或更改默认值时都更改打印代码。

谢谢!

I have a flags enum defined like this:

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

I would like a way to print out which flags are included in MyEnum.Default. In this case, I'd want the output to be something like "Choice1, Choice2".

The problem with simply printing MyEnum.Default.ToString() is that the output would be "Default" when I want "Choice1, Choice2".

Here's one option, but if I used this I'd have to update the printing every time I changed the enum.

((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() : "")

Does anyone have a cleaner way of doing this? Ideally, I'd like a way of printing out the flags included in MyEnum.Default without having to change the printing code every time I added a new flag or changed the default.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

无法言说的痛 2024-11-06 07:12:50

使用我在此处相关的扩展方法问题,这应该很简单:

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

Using the extension methods I've written here on a related question, this should be simple:

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

And here's the extension methods:

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;
        }
    }
}
甜嗑 2024-11-06 07:12:50

使用 FlagsAttribute 装饰您的枚举。它所做的几乎正是您所追求的:

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

FooNum f = FooNum.bar | FooNum.borkbork;

Debug.WriteLine(f.ToString());

应该给您:

bar,borkbork

Decorate your enum with FlagsAttribute. It does pretty much exactly what you're after:

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

FooNum f = FooNum.bar | FooNum.borkbork;

Debug.WriteLine(f.ToString());

should give you:

bar, borkbork

木有鱼丸 2024-11-06 07:12:50

使用flags.ToString("g");
请参阅枚举格式字符串

Use flags.ToString("g");
See Enumeration Format Strings

樱娆 2024-11-06 07:12:50

通过单个 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

Print by single linq statement:

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

Updated version to print only explicitly defined values:

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
知足的幸福 2024-11-06 07:12:50
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();
        }
    }
}
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();
        }
    }
}
静若繁花 2024-11-06 07:12:50

我用最短、最清晰的代码解决了这个问题,我希望它能表现良好,尽管在几个地方有拳击。以您的类型为例:

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 可以转换为其他类型 TintEnum

我认为这可以通过使用临时变量在循环内强制转换为 int 来优化。

我想是的,是的。为了清楚起见,代码是这样写的。所以,是的,如果您愿意,可以这样做并摆脱那些 HasFlag 调用。

我认为您仍然可以使用 Enum 作为 foreach 变量并节省转换。

不可以,因为您需要强制转换为 T,而这只能从 object 完成。可能有“更好”的解决方案,但这肯定是最短、最清晰的解决方案。

I solved this in the shortest, clearest code possible that I expect performs well, although there is boxing in a couple of places. Using your type as an example:

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

This is how it's implemented:

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

Some comments might come up and I'll address these first:

I need to call your method providing both type and variable?

Because this can't be done with a generic T argument implicitly. T can't be cast to Enum for use with HasFlag. No, also not using where T : struct, IConvertible.

The foreach also uses object?

Yes, also to be able to cast. Only object can be cast to the other types T, int, Enum.

I think this can be optimized by casting to int inside the loop once with a temporary variable.

I think so, yes. This code was written like this for clarity. So yes do that and get rid of those HasFlag calls if you like.

I think you still can use Enum as the foreach variable and save on casting.

No, because you need a cast to T and that can only be done from object. There might be 'better' solutions but this is most certainly the shortest and clearest one.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文