Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Most efficient way to parse a flagged enum to a list

Tags:

c#

enums

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.

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.

like image 273
David Neale Avatar asked Sep 08 '10 14:09

David Neale


People also ask

How do I turn an enum into a list?

The idea is to use the Enum. GetValues() method to get an array of the enum constants' values. To get an IEnumerable<T> of all the values in the enum, call Cast<T>() on the array. To get a list, call ToList() after casting.

What is enum parse?

The Parse method in Enum converts the string representation of the name or numeric value of enum constants to an equivalent enumerated object.

What is a flag enum?

The idea of Enum Flags is to take an enumeration variable and allow it hold multiple values. It should be used whenever the enum represents a collection of flags, rather than representing a single value. Such enumeration collections are usually manipulated using bitwise operators.

How does enum parse work?

Parse(Type, String, Boolean) Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object. A parameter specifies whether the operation is case-insensitive.


6 Answers

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());
  }
}
like image 137
Grzenio Avatar answered Sep 30 '22 18:09

Grzenio


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. 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();

like image 22
Mick Bruno Avatar answered Sep 30 '22 18:09

Mick Bruno


Enum.Parse will handle the concatenated values outputted by ToString just fine. 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).

like image 37
Alex Paven Avatar answered Sep 30 '22 19:09

Alex Paven


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
}
like image 22
Dmytro Strembetskyi Avatar answered Sep 30 '22 19:09

Dmytro Strembetskyi


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
}
like image 45
Darin Dimitrov Avatar answered Sep 30 '22 18:09

Darin Dimitrov


Similar answer to Mick's but puts the operations into extensions and fixes/cleans up the extra space character (from the split).

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
}
like image 28
ΩmegaMan Avatar answered Sep 30 '22 18:09

ΩmegaMan