Tutorial :Enum ToString with user friendly strings



Question:

My enum consists of the following values:

private enum PublishStatusses{      NotCompleted,      Completed,      Error  };  

I want to be able to output these values in a user friendly way though.
I don't need to be able to go from string to value again.


Solution:1

I use the Description attribute from the System.ComponentModel namespace. Simply decorate the enum:

private enum PublishStatusValue  {      [Description("Not Completed")]      NotCompleted,      Completed,      Error  };  

Then use this code to retrieve it:

public static string GetDescription<T>(this T enumerationValue)      where T : struct  {      Type type = enumerationValue.GetType();      if (!type.IsEnum)      {          throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");      }        //Tries to find a DescriptionAttribute for a potential friendly name      //for the enum      MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());      if (memberInfo != null && memberInfo.Length > 0)      {          object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);            if (attrs != null && attrs.Length > 0)          {              //Pull out the description value              return ((DescriptionAttribute)attrs[0]).Description;          }      }      //If we have no description attribute, just return the ToString of the enum      return enumerationValue.ToString();  }  


Solution:2

I do this with extension methods:

public enum ErrorLevel  {    None,    Low,    High,    SoylentGreen  }    public static class ErrorLevelExtensions  {    public static string ToFriendlyString(this ErrorLevel me)    {      switch(me)      {        case ErrorLevel.None:          return "Everything is OK";        case ErrorLevel.Low:          return "SNAFU, if you know what I mean.";        case ErrorLevel.High:          return "Reaching TARFU levels";        case ErrorLevel.SoylentGreen:          return "ITS PEOPLE!!!!";        default:          return "Get your damn dirty hands off me you FILTHY APE!";      }    }  }  


Solution:3

Maybe I'm missing something, but what's wrong with Enum.GetName?

public string GetName(PublishStatusses value)  {    return Enum.GetName(typeof(PublishStatusses), value)  }  

edit: for user-friendly strings, you need to go through a .resource to get internationalisation/localisation done, and it would arguably be better to use a fixed key based on the enum key than a decorator attribute on the same.


Solution:4

The easiest solution here is to use a custom extension method (in .NET 3.5 at least - you can just convert it into a static helper method for earlier framework versions).

public static string ToCustomString(this PublishStatusses value)  {      switch(value)      {          // Return string depending on value.      }      return null;  }  

I am assuming here that you want to return something other than the actual name of the enum value (which you can get by simply calling ToString).


Solution:5

I created a reverse extension method to convert the description back into an enum value:

    public static T ToEnumValue<T>(this string enumerationDescription) where T : struct      {          Type type = typeof(T);          if (!type.IsEnum)              throw new ArgumentException("ToEnumValue<T>(): Must be of enum type", "T");          foreach (object val in System.Enum.GetValues(type))              if (val.GetDescription<T>() == enumerationDescription)                  return (T)val;          throw new ArgumentException("ToEnumValue<T>(): Invalid description for enum " + type.Name, "enumerationDescription");      }  


Solution:6

That other post is Java. You can't put methods in Enums in C#.

just do something like this:

PublishStatusses status = ...  String s = status.ToString();  

If you want to use different display values for your enum values, you could use Attributes and Reflection.


Solution:7

Some other more primitive options that avoid classes/reference types:

  • Array method
  • Nested struct method

Array method

private struct PublishStatusses  {      public static string[] Desc = {          "Not Completed",          "Completed",          "Error"      };        public enum Id      {          NotCompleted = 0,          Completed,          Error      };  }  

Usage

string desc = PublishStatusses.Desc[(int)PublishStatusses.Id.Completed];  

Nested struct method

private struct PublishStatusses  {      public struct NotCompleted      {          public const int Id = 0;          public const string Desc = "Not Completed";      }        public struct Completed      {          public const int Id = 1;          public const string Desc = "Completed";      }        public struct Error      {          public const int Id = 2;          public const string Desc = "Error";      }              }  

Usage

int id = PublishStatusses.NotCompleted.Id;  string desc = PublishStatusses.NotCompleted.Desc;  

Update (03/09/2018)

A hybrid of Extension Methods and the first technique above.

I prefer enums to be defined where they "belong" (closest to their source of origin and not in some common, global namespace).

namespace ViewModels  {      public class RecordVM      {          //public enum Enum { Minutes, Hours }          public struct Enum          {              public enum Id { Minutes, Hours }              public static string[] Name = { "Minute(s)", "Hour(s)" };          }      }  }  

The extension method seems suited for a common area, and the "localized" definition of the enum now makes the extension method more verbose.

namespace Common  {      public static class EnumExtensions      {          public static string Name(this RecordVM.Enum.Id id)          {              return RecordVM.Enum.Name[(int)id];          }      }     }  

A usage example of the enum and it's extension method.

namespace Views  {      public class RecordView       {          private RecordDataFieldList<string, string> _fieldUnit;            public RecordView()          {              _fieldUnit.List = new IdValueList<string, string>              {                              new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()),                  new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name())              };          }            private void Update()          {                  RecordVM.Enum.Id eId = DetermineUnit();                _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value;          }      }  }  

Note: I actually decided to eliminate the Enum wrapper (and Name array), since it's best that the name strings come from a resource (ie config file or DB) instead of being hard-coded, and because I ended up putting the extension method in the ViewModels namespace (just in a different, "CommonVM.cs" file). Plus the whole .Id thing becomes distracting and cumbersome.

namespace ViewModels  {      public class RecordVM      {          public enum Enum { Minutes, Hours }          //public struct Enum          //{          //    public enum Id { Minutes, Hours }          //    public static string[] Name = { "Minute(s)", "Hour(s)" };          //}      }  }  

CommonVM.cs

//namespace Common  namespace ViewModels  {      public static class EnumExtensions      {          public static string Name(this RecordVM.Enum id)          {              //return RecordVM.Enum.Name[(int)id];              switch (id)              {                  case RecordVM.Enum.Minutes: return "Minute(s)";                                      case RecordVM.Enum.Hours: return "Hour(s)";                  default: return null;              }          }      }     }  

A usage example of the enum and it's extension method.

namespace Views  {      public class RecordView       {          private RecordDataFieldList<string, string> _fieldUnit            public RecordView()          {              _fieldUnit.List = new IdValueList<string, string>              {                              new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()),                  new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name())              };          }            private void Update()          {                  RecordVM.Enum eId = DetermineUnit();                _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value;          }      }  }  


Solution:8

With respect to Ray Booysen, there is a bug in the code: Enum ToString with user friendly strings

You need to account for multiple attributes on the enum values.

public static string GetDescription<T>(this object enumerationValue)              where T : struct      {          Type type = enumerationValue.GetType();          if (!type.IsEnum)          {              throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");          }            //Tries to find a DescriptionAttribute for a potential friendly name          //for the enum          MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());          if (memberInfo != null && memberInfo.Length > 0)          {              object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);                if (attrs != null && attrs.Length > 0 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null)              {                  //Pull out the description value                  return ((DescriptionAttribute)attrs.Where(t=>t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description;              }          }          //If we have no description attribute, just return the ToString of the enum          return enumerationValue.ToString();  


Solution:9

public enum MyEnum  {      [Description("Option One")]      Option_One  }    public static string ToDescriptionString(this Enum This)  {      Type type = This.GetType();        string name = Enum.GetName(type, This);        MemberInfo member = type.GetMembers()          .Where(w => w.Name == name)          .FirstOrDefault();        DescriptionAttribute attribute = member != null          ? member.GetCustomAttributes(true)              .Where(w => w.GetType() == typeof(DescriptionAttribute))              .FirstOrDefault() as DescriptionAttribute          : null;        return attribute != null ? attribute.Description : name;  }  


Solution:10

The simplest way is just to include this extension class into your project, it will work with any enum in the project:

public static class EnumExtensions  {      public static string ToFriendlyString(this Enum code)      {          return Enum.GetName(code.GetType(), code);      }  }  

Usage:

enum ExampleEnum  {      Demo = 0,      Test = 1,       Live = 2  }  

...

ExampleEnum ee = ExampleEnum.Live;  Console.WriteLine(ee.ToFriendlyString());  


Solution:11

You can use Humanizer package with Humanize Enums possiblity. An eaxample:

enum PublishStatusses  {      [Description("Custom description")]      NotCompleted,      AlmostCompleted,      Error  };  

then you can use Humanize extension method on enum directly:

var st1 = PublishStatusses.NotCompleted;  var str1 = st1.Humanize(); // will result in Custom description    var st2 = PublishStatusses.AlmostCompleted;  var str2 = st2.Humanize(); // will result in Almost completed (calculated automaticaly)  


Solution:12

Clean summary of the above suggestions with sample:

namespace EnumExtensions {    using System;  using System.Reflection;    public class TextAttribute : Attribute {     public string Text;     public TextAttribute( string text ) {        Text = text;     }//ctor  }// class TextAttribute    public static class EnumExtender {    public static string ToText( this Enum enumeration ) {       MemberInfo[] memberInfo = enumeration.GetType().GetMember( enumeration.ToString() );       if ( memberInfo != null && memberInfo.Length > 0 ) {          object[] attributes = memberInfo[ 0 ].GetCustomAttributes( typeof(TextAttribute),  false );          if ( attributes != null && attributes.Length > 0 ) {           return ( (TextAttribute)attributes[ 0 ] ).Text;        }       }//if       return enumeration.ToString();    }//ToText    }//class EnumExtender    }//namespace  

USAGE:

using System;  using EnumExtensions;    class Program {    public enum Appearance {      [Text( "left-handed" ) ]    Left,      [Text( "right-handed" ) ]    Right,    }//enum    static void Main( string[] args ) {       var appearance = Appearance.Left;     Console.WriteLine( appearance.ToText() );    }//Main    }//class  


Solution:13

Instead of using an enum use a static class.

replace

private enum PublishStatuses{      NotCompleted,      Completed,      Error  };  

with

private static class PublishStatuses{      public static readonly string NotCompleted = "Not Completed";      public static readonly string Completed = "Completed";      public static readonly string Error = "Error";  };  

it will be used like this

PublishStatuses.NotCompleted; // "Not Completed"  

Issue using the top "extension method" solutions:

A private enum is often used inside another class. The extension method solution is not valid there since it must be in it's own class. This solution can be private and embedded in another class.


Solution:14

This is an update to Ray Booysen's code that uses the generic GetCustomAttributes method and LINQ to make things a bit tidier.

    /// <summary>      /// Gets the value of the <see cref="T:System.ComponentModel.DescriptionAttribute"/> on an struct, including enums.        /// </summary>      /// <typeparam name="T">The type of the struct.</typeparam>      /// <param name="enumerationValue">A value of type <see cref="T:System.Enum"/></param>      /// <returns>If the struct has a Description attribute, this method returns the description.  Otherwise it just calls ToString() on the struct.</returns>      /// <remarks>Based on http://stackoverflow.com/questions/479410/enum-tostring/479417#479417, but useful for any struct.</remarks>      public static string GetDescription<T>(this T enumerationValue) where T : struct      {          return enumerationValue.GetType().GetMember(enumerationValue.ToString())                  .SelectMany(mi => mi.GetCustomAttributes<DescriptionAttribute>(false),                      (mi, ca) => ca.Description)                  .FirstOrDefault() ?? enumerationValue.ToString();      }     


Solution:15

I happen to be a VB.NET fan, so here's my version, combining the DescriptionAttribute method with an extension method. First, the results:

Imports System.ComponentModel ' For <Description>    Module Module1    ''' <summary>    ''' An Enum type with three values and descriptions    ''' </summary>    Public Enum EnumType      <Description("One")>      V1 = 1        ' This one has no description      V2 = 2        <Description("Three")>      V3 = 3    End Enum      Sub Main()      ' Description method is an extension in EnumExtensions      For Each v As EnumType In [Enum].GetValues(GetType(EnumType))        Console.WriteLine("Enum {0} has value {1} and description {2}",          v,          CInt(v),          v.Description        )      Next      ' Output:      ' Enum V1 has value 1 and description One      ' Enum V2 has value 2 and description V2      ' Enum V3 has value 3 and description Three    End Sub  End Module  

Basic stuff: an enum called EnumType with three values V1, V2 and V3. The "magic" happens in the Console.WriteLine call in Sub Main(), where the last argument is simply v.Description. This returns "One" for V1, "V2" for V2, and "Three" for V3. This Description-method is in fact an extension method, defined in another module called EnumExtensions:

Option Strict On  Option Explicit On  Option Infer Off    Imports System.Runtime.CompilerServices  Imports System.Reflection  Imports System.ComponentModel    Module EnumExtensions    Private _Descriptions As New Dictionary(Of String, String)      ''' <summary>    ''' This extension method adds a Description method    ''' to all enum members. The result of the method is the    ''' value of the Description attribute if present, else    ''' the normal ToString() representation of the enum value.    ''' </summary>    <Extension>    Public Function Description(e As [Enum]) As String      ' Get the type of the enum      Dim enumType As Type = e.GetType()      ' Get the name of the enum value      Dim name As String = e.ToString()        ' Construct a full name for this enum value      Dim fullName As String = enumType.FullName + "." + name        ' See if we have looked it up earlier      Dim enumDescription As String = Nothing      If _Descriptions.TryGetValue(fullName, enumDescription) Then        ' Yes we have - return previous value        Return enumDescription      End If        ' Find the value of the Description attribute on this enum value      Dim members As MemberInfo() = enumType.GetMember(name)      If members IsNot Nothing AndAlso members.Length > 0 Then        Dim descriptions() As Object = members(0).GetCustomAttributes(GetType(DescriptionAttribute), False)        If descriptions IsNot Nothing AndAlso descriptions.Length > 0 Then          ' Set name to description found          name = DirectCast(descriptions(0), DescriptionAttribute).Description        End If      End If        ' Save the name in the dictionary:      _Descriptions.Add(fullName, name)        ' Return the name      Return name    End Function  End Module  

Because looking up description attributes using Reflection is slow, the lookups are also cached in a private Dictionary, that is populated on demand.

(Sorry for the VB.NET solution - it should be relatively straighforward to translate it to C#, and my C# is rusty on new subjects like extensions)


Solution:16

Even cleaner summary:

using System;  using System.Reflection;    public class TextAttribute : Attribute  {      public string Text;      public TextAttribute(string text)      {          Text = text;      }  }      public static class EnumExtender  {      public static string ToText(this Enum enumeration)      {          var memberInfo = enumeration.GetType().GetMember(enumeration.ToString());          if (memberInfo.Length <= 0) return enumeration.ToString();            var attributes = memberInfo[0].GetCustomAttributes(typeof(TextAttribute), false);          return attributes.Length > 0 ? ((TextAttribute)attributes[0]).Text : enumeration.ToString();      }  }  

Same usage as underscore describes.


Solution:17

For flags enum including.

    public static string Description(this Enum value)      {          Type type = value.GetType();            List<string> res = new List<string>();          var arrValue = value.ToString().Split(',').Select(v=>v.Trim());          foreach (string strValue in arrValue)          {              MemberInfo[] memberInfo = type.GetMember(strValue);              if (memberInfo != null && memberInfo.Length > 0)              {                  object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);                    if (attrs != null && attrs.Length > 0 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null)                  {                      res.Add(((DescriptionAttribute)attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description);                  }                  else                      res.Add(strValue);              }              else                  res.Add(strValue);          }            return res.Aggregate((s,v)=>s+", "+v);      }  


Solution:18

I think the best (and easiest) way to solve your problem is to write an Extension-Method for your enum:

public static string GetUserFriendlyString(this PublishStatusses status)      {        }  


Solution:19

If you want something completely customizable, try out my solution here:

http://www.kevinwilliampang.com/post/Mapping-Enums-To-Strings-and-Strings-to-Enums-in-NET.aspx

Basically, the post outlines how to attach Description attributes to each of your enums and provides a generic way to map from enum to description.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »