繁体   English   中英

将标记的枚举解析为列表的最有效方法

[英]Most efficient way to parse a flagged enum to a list

我有一个带标记的枚举,需要检索其上设置的所有值的名称。

我目前正在利用枚举的 ToString() 方法,该方法返回以逗号分隔的元素。

public void SetRoles(Enums.Roles role)
{
    IList<Entities.Role> roleList = role.ToString("G").Split(',')
        .Select(r => new Entities.Role(r.Trim()))
        .ToList();
    ...
}

我相信一定有比这更好的方法。

试试这个:

public void SetRoles(Enums.Roles role)
{
  List<string> result = new List<string>();
  foreach(Roles r in Enum.GetValues(typeof(Roles)))
  {
    if ((role & r) != 0) result.Add(r.ToString());
  }
}

如果您真的只想要字符串,那么没有比以下更简单的了:

string[] flags = role.ToString().Split(',');

这比使用 LINQ 更简单,并且仍然只是一行代码。 或者,如果您想要一个列表而不是问题示例中的数组,您可以将数组转换为列表:

List<string> flags = new List<string>(role.ToString().Split(','));

在我的情况下,我需要一个通用的解决方案并想出了这个:

value.ToString().Split(',').Select(flag => (T)Enum.Parse(typeof(T), flag)).ToList();

Enum.Parse 将处理由 ToString 输出的连接值就好了。 使用立即窗口证明:

? System.Enum.Parse(typeof(System.AttributeTargets), "Class, Enum")
Class | Enum

(第二行是输出,它在调试器/立即窗口中与通用 Enum.ToString() 输出不同)。

为什么需要清单? 一切都已经存储在标志中:

[Flags]
enum Roles
{
    Read = 0x1,
    Write = 0x2,
    Delete = 0x4,
}

然后分配角色:

var roles = Roles.Read | Roles.Write;

每当您需要检查给定角色是否已存在时,您都不需要查看列表,而只需查看角色枚举:

if ((roles & Roles.Read) == Roles.Read)
{
    // The user has read permission
}
if ((roles & Roles.Write) == Roles.Write)
{
    // The user has write permission
}
List<string> GetRoleNames(Roles roles) =>
    Enum.GetValues(typeof(Roles))
        .Cast<Roles>()
        .Where(role => roles.HasFlag(role))
        .Select(role => role.ToString())
        .ToList();

void TestRoleSelection()
{
    var selectedRoles = (Roles)6;
    var roleNames = GetRoleNames(selectedRoles);
    Console.WriteLine(string.Join(",", roleNames));
    // Output: Admin,User
}

[Flags]
enum Roles
{
    SuperAdmin = 1,
    Admin = 2,
    User = 4,
    Anonymous = 8
}

与 Mick 的答案类似,但将操作放入扩展中并修复/清理额外的空格字符(来自拆分)。

如果枚举中有_ ,代码也会将其更改为空格。

public static class EnumExtensions
{  
    // Take anded flag enum and extract the cleaned string values.
    public static List<string> ToComparableStrings(this Enum eNum)
        =>  eNum.ToString()
                .Split(',')
                .Select(str => str.ToCleanString())
                .ToList();

    // Take an individual enum and report the textual value.
    public static string ToComparableString(this Enum eNum)
        => eNum.ToString()
               .ToCleanString();

    // Remove any spaces due to split and if `_` found change it to space.
    public static string ToCleanString(this string str)
        => str.Replace(" ", string.Empty)
              .Replace('_', ' ');
}

用法

var single   = PivotFilter.Dollars_Only;
var multiple = PivotFilter.Dollars_Only | PivotFilter.Non_Productive;

                                // These calls return:
single.ToComparableString()     // "Dollars Only"
multiple.ToComparableString()   // "Non Productive,Dollars Only"
multiple.ToComparableStrings()  // List<string>() { "Non Productive", "Dollars Only" }

使用枚举

[Flags]
// Define other methods, classes and namespaces here
public enum PivotFilter
{
    Agency = 1,
    Regular = 2,
    Overtime = 4,
    Non_Productive = 8,
    Dollars_Only = 16,
    Ignore = 32
}

标记的枚举转换为列表可能不像看起来那么简单。 以下面的枚举为例:

[Flags]
enum MenuItems
{
    None = 0,
    Pizza = 1,
    Fries = 2,
    Pancakes = 4,
    Meatballs = 8,
    Pasta = 16,
    StuffWithP = Pizza | Pancakes | Pasta,
    All = Pizza | Fries | Pancakes | Meatballs | Pasta | StuffWithP
};

如果我们有值StuffWithP ,我们想要列表中的什么? StuffWithP还是Pizza, Pancakes, Pasta 我在女巫中有一个用例,我需要将枚举值“解构”到各个标志并将它们放在列表中。 我想出了以下内容:

public static string[] DeconstructFlags(Enum items)
{
    if (items.GetType().GetCustomAttribute<FlagsAttribute>() == null)
    {
        throw new ArgumentException("Enum has no [Flags] attribute.", nameof(items));
    }

    // no value, no list
    var itemsValue = (int)(object)items;
    if (itemsValue == 0) return Array.Empty<string>();

    var result = new List<string>();

    foreach (var item in Enum.GetValues(items.GetType()))
    {
        if(item == null) continue;

        var value = (int)item;

        // skip combined flags
        if (!BitOperations.IsPow2(value))
        {
            continue;
        }

        if (items.HasFlag((Enum)item))
        {
            result.Add(item.ToString() ?? "");
        }
    }

    return result.ToArray();
}

我不知道它是否是最有效的,但它很好地跳过了这些组合标志。 在我的博客上写了更多内容: 解构 [Flags] 枚举

F#版本

module Enum =

    let inline flagsToList< ^e when ^e: equality and ^e: (static member (&&&): ^e * ^e -> ^e)> value =
        Enum.GetValues(typeof<^e>)
        |> Seq.cast<^e>
        |> Seq.where (fun case -> case &&& value = case)
        |> Seq.toList

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM