13

I have a class "license" which is a collection of a bunch of enum flags like this:

Public class License
{
    UsageType Usage { get; set; }
    PlatformType Platform { get; set; }

    public enum UsageType { read = 1, write = 2, wipe = 4, all = 7 }
    public enum PlatformType { windows = 1, linux = 2, ios = 4, all = 7 }

    etc...
}

The point is that the various flags of the same category can be OR'd together to form a profile of what the user can do with said license. Now I'm trying to display the values of "Usage" and "Platform" in a human-friendly way so for instance if Usage == UsageType.read | UsageType.write then it should be parsed to "read, write".

I did this successfully with a single enum type by testing the value for each flag and appending enumitem.ToString() for each flag it has to a string. Since I have a lot of these enums and values though, I'd like to come up with a more generic approach.

I came up with this (below) but since I'm not very familiar with template functions in c# so I don't know why this doesn't work but at least it should illustrate what i mean:

private string parseEnum<T>(T value)
{
    string ret = "";
    foreach (var ei in (T[])Enum.GetValues(typeof(T)))
    {
        if (value.HasFlag(ei)) ret += ei.ToString() + ", ";
    }
    ret = ret.substring(0, ret.Length-1);
    return ret;
}

It's saying that T does not contain a definition for "HasFlag" but how could it now that if it doesn't know what T is?

Stígandr
  • 2,874
  • 21
  • 36
user81993
  • 6,167
  • 6
  • 32
  • 64
  • Not exactly a duplicate, but the problem you're encountering is essentially this: http://stackoverflow.com/questions/79126/create-generic-method-constraining-t-to-an-enum. The solution provided to that question should get you where you need to be. – Jim Mischel Oct 01 '14 at 20:18
  • http://msdn.microsoft.com/en-us/library/system.flagsattribute(v=vs.110).aspx or http://stackoverflow.com/questions/8447/what-does-the-flags-enum-attribute-mean-in-c – Mephy Oct 01 '14 at 20:18
  • Have you tried `enumValue.ToString()`? – jrummell Oct 01 '14 at 20:22
  • 1
    If you use the [`Flags`](http://msdn.microsoft.com/en-us/library/system.flagsattribute%28v=vs.110%29.aspx) attribute on your enum, you get nice printing (that lists each constituent flag that is set) for free. – Asad Saeeduddin Oct 01 '14 at 20:23
  • 2
    As an aside, [`string.Join`](http://msdn.microsoft.com/en-us/library/system.string.join(v=vs.110).aspx) is a great method for preventing ugly string-building loops. – Tim S. Oct 01 '14 at 20:30

1 Answers1

49

You should use the FlagsAttribute, which causes the built-in ToString and Enum.Parse methods to work just the way you want. Also note that the convention is that flags enum names should be plural, so e.g. UsageTypes instead of UsageType.

[Flags]
public enum UsageTypes { Read = 1, Write = 2, Wipe = 4, All = 7 }
[Flags]
public enum PlatformTypes { Windows = 1, Linux = 2, iOs = 4, All = 7 }

var e1 = License.UsageTypes.Read | License.UsageTypes.Write;
var s = e1.ToString();
Debug.Assert(s == "Read, Write");
var e2 = (License.UsageTypes)Enum.Parse(typeof(License.UsageTypes), s);
Debug.Assert(e1 == e2);
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
Tim S.
  • 55,448
  • 7
  • 96
  • 122
  • 3
    Talking about naming conventions, enum fields [should be in Pascal Case](https://msdn.microsoft.com/en-us/library/ms229043). – Shimmy Weitzhandler May 02 '17 at 14:48
  • Doesn't work if `e1 = License.UsageTypes.All` s == "All" not "Read, Write, Wipe" – Kabua Feb 14 '20 at 17:28
  • I think that was Tim's intent, hence why he set the value of `All` to 7 instead of 8 (which would have made it a unique value, rather than cumulative). It represents the combination of the previous 3 options. `(UsageTypes.Read | UsageTypes.Write | UsageTypes.Wipe).ToString("F")` evaluates to `"All"`. If you want to always see the individual options selected, just remove `All` from the enum. – J.D. Mallen Feb 03 '21 at 11:21