[英]Most efficient way to parse a flagged enum to a list
I have a flagged enum and need to retrieve the names of all values set on it.我有一个带标记的枚举,需要检索其上设置的所有值的名称。
I am currently taking advantage of the enum's ToString() method which returns the elements comma-separated.我目前正在利用枚举的 ToString() 方法,该方法返回以逗号分隔的元素。
public void SetRoles(Enums.Roles role)
{
IList<Entities.Role> roleList = role.ToString("G").Split(',')
.Select(r => new Entities.Role(r.Trim()))
.ToList();
...
}
I'm sure there must be a better way than this.我相信一定有比这更好的方法。
Try this:试试这个:
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());
}
}
If you genuinely just want the strings, can't get much simpler than:如果您真的只想要字符串,那么没有比以下更简单的了:
string[] flags = role.ToString().Split(',');
This is simpler than using LINQ and is still just a single line of code.这比使用 LINQ 更简单,并且仍然只是一行代码。 Or if you want a list instead of an array as in the sample in the question you can convert the array into a list:或者,如果您想要一个列表而不是问题示例中的数组,您可以将数组转换为列表:
List<string> flags = new List<string>(role.ToString().Split(','));
In my case I needed a generic solution and came up with this:在我的情况下,我需要一个通用的解决方案并想出了这个:
value.ToString().Split(',').Select(flag => (T)Enum.Parse(typeof(T), flag)).ToList();
Enum.Parse will handle the concatenated values outputted by ToString just fine. Enum.Parse 将处理由 ToString 输出的连接值就好了。 Proof using the Immediate window:使用立即窗口证明:
? System.Enum.Parse(typeof(System.AttributeTargets), "Class, Enum")
Class | Enum
(the second line is the output, which is different in the debugger/immediate window from the generic Enum.ToString() output). (第二行是输出,它在调试器/立即窗口中与通用 Enum.ToString() 输出不同)。
Why do you need a list?为什么需要清单? Everything is already stored in the flags:一切都已经存储在标志中:
[Flags]
enum Roles
{
Read = 0x1,
Write = 0x2,
Delete = 0x4,
}
Then assign roles:然后分配角色:
var roles = Roles.Read | Roles.Write;
And whenever you need to check if a given role has been you don't need to look in a list, but simply look in the roles enumeration:每当您需要检查给定角色是否已存在时,您都不需要查看列表,而只需查看角色枚举:
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
}
Similar answer to Mick's but puts the operations into extensions and fixes /cleans up the extra space character (from the split).与 Mick 的答案类似,但将操作放入扩展中并修复/清理额外的空格字符(来自拆分)。
Also as a bonus if the enum has a _
in it, the code changes it to a space.如果枚举中有_
,代码也会将其更改为空格。
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('_', ' ');
}
Usage用法
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" }
Enum for Usage使用枚举
[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
}
Turning a flagged enum to a list might not be as straight forward as it looks.将标记的枚举转换为列表可能不像看起来那么简单。 Take the following enum for example:以下面的枚举为例:
[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
};
If we have the value StuffWithP
, what do we want in the list?如果我们有值StuffWithP
,我们想要列表中的什么? StuffWithP
or Pizza, Pancakes, Pasta
? StuffWithP
还是Pizza, Pancakes, Pasta
? I had a use case in witch I needed to "deconstruct" the enum value to the invidual flags and put those in a list.我在女巫中有一个用例,我需要将枚举值“解构”到各个标志并将它们放在列表中。 I came up with the following:我想出了以下内容:
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();
}
I don't know if it is the most efficient , but it skips those combined flags nicely.我不知道它是否是最有效的,但它很好地跳过了这些组合标志。 Wrote some more on my blog: Deconstructing a [Flags] enum .在我的博客上写了更多内容: 解构 [Flags] 枚举。
F# version 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.