365

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.

Boris Callens
  • 90,659
  • 85
  • 207
  • 305
  • 1
    possible duplicate of [C# String enums](http://stackoverflow.com/questions/424366/c-sharp-string-enums) – nawfal Jun 08 '13 at 22:51

26 Answers26

439

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();
}
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Ray Booysen
  • 28,894
  • 13
  • 84
  • 111
  • just noticed the actual extension method never used the constrained type parameter. fixed it to where the parameter uses the constrained type instead of object. – nathan gonzalez Jul 08 '13 at 04:05
  • 17
    This example is easier to read. http://stackoverflow.com/questions/1415140/can-my-enums-have-friendly-names – RayLoveless Apr 08 '14 at 22:01
  • 43
    I suspect there is a significant performance hit for using reflection as described in this solution. The code for Will's method of using the ToFriendlyString extension method is much easier to understand, and its performance should be extremely fast too. – humbads Apr 09 '14 at 18:52
  • 1
    I like the version that @RayL linked as it will only add the extension method to Enums. If that's all you want to use this for (as indicated with the `ArgumentException`, then there's no reason to have the method be completely generic. – krillgar Aug 04 '14 at 12:32
  • 5
    It does mean that every enum needs it's own extension method. This is more general use and does require more work, but you'd probably want to quantify what "fast" means before we decide on the performance. – Ray Booysen Mar 30 '15 at 16:07
  • 1
    Additionally, you could write some, potentially horrific, code to cache the values. Since they're compiled in, you could statically cache the values of the description attributes in something like a static Dictionary dictionary outside the extension method. Depends on your thinking around the code smell here. – Ray Booysen Mar 30 '15 at 16:13
  • @RayBooysen It works perfectly, but I cannot bind Description values to DropDownListFor. How to bind? – Jack Dec 16 '15 at 17:26
  • @christof, why can you not bind? What issues are you seeing? – Ray Booysen Dec 17 '15 at 08:43
  • @RayBooysen Thanks for reply. Finally I managed to bind to Kendo DropdownListFor, but I am unsure if there is unnecessary part in .Bind section? `.BindTo(Enum.GetValues(typeof(MvcPWy.Enums.ReasonOfVisit)).Cast().Select(x => new SelectListItem { Text = x.GetDescription(), Value = x.ToString() }))` – Jack Dec 17 '15 at 09:37
  • @christof, apologies I am not familiar with that component. It looks fine, but does the Value property need to be a string? – Ray Booysen Dec 23 '15 at 15:35
  • @RayBooysen Actually Description value should be string. – Jack Dec 23 '15 at 15:36
  • Absolutely no point to making this method generic. Replace "this T" with "this Enum". – Mr Anderson May 26 '16 at 15:42
  • GetDescription() function is relevant just use MyEnum.MY_TYPE.ToString() – Pitka Aug 31 '16 at 13:32
  • 2
    @petar that works but not if you want friendly strings to be displayed to users. MY_TYPE will have the underscore and is not customisable. – Ray Booysen Aug 31 '16 at 21:31
  • 1
    IsEnum depends on the framework. I had to use `if(!type.GetTypeInfo().IsEnum)` – Thom Kiesewetter Dec 28 '16 at 11:08
  • What is the significance of `struct` in the constraint on type `T`? Does this mean that all enumerator values come in as a `struct`? – void.pointer Jan 23 '20 at 22:57
  • Simply decorate the numb? This looks like a horrifically complex way of mapping enums to a description string. – Chris Hatton Apr 27 '20 at 04:03
  • 1
    Thanks Chris. Insightful. Do you have another idea? Pop it in the answers so others can learn – Ray Booysen Apr 27 '20 at 08:09
  • We can use also `EnumMember` from `System.Runtime.Serialization`, if the string is more _symbolic_. – gsscoder Jul 21 '20 at 06:35
428

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!";
    }
  }
}
  • yes, thanks for the input though. I put some remarks in noldorim's post regarding this solution. – Boris Callens Jan 26 '09 at 11:36
  • 10
    This is so much cleaner than the Attribute answer. Nice! – pennyrave May 06 '12 at 18:27
  • 4
    @pennyrave: Eh. Lots of UI components are expecting to find and use DisplayNameAttribute and DescriptionAttribute. In fact, now, I use these and an extension method to easily get those values out. –  May 06 '12 at 18:30
  • 81
    The issue I see with this is that you're constantly writing these extension methods. With the attribute mechanism, it's a simple way of decorating it and only ever calling one method. – Ray Booysen Jan 07 '14 at 17:50
  • 1
    @RayBooysen: psst http://stackoverflow.com/questions/479410/enum-tostring/479453?noredirect=1#comment13530613_479453 –  Jan 07 '14 at 18:12
  • 6
    Not sure what you mean? – Ray Booysen Jan 09 '14 at 18:33
  • 1
    @RayBooysen: Re: your comment, if you read up the list of comments (or click on that comment link) you'll see that I now agree with you. –  Jan 09 '14 at 18:47
  • 1
    More about extension methods here: http://msdn.microsoft.com/en-us/library/bb383974.aspx – Doug Jul 04 '14 at 14:29
  • I like this way, it's simple and the string values can be seen in the same file. – Héctor Espí Hernández Oct 22 '15 at 07:04
  • On a side not that code doesn't compile as there's no default for the switch statement, http://stackoverflow.com/questions/2071345/why-do-not-all-code-paths-return-a-value-with-a-switch-statement-and-an-enum – Moon Waxing Sep 30 '16 at 03:35
  • 1
    @MoonWaxing fixed. Also, note the "c#-like pseudocode" note in my profile. –  Sep 30 '16 at 12:59
  • @Panzercrisis it's a movie quote. Search for it. Also, damn damned damnit damn damn damnitol. There. No point editing it now. –  Jan 09 '17 at 21:40
  • 13
    It's better, in my opinion, to allow the `default` case implementation to return `me.ToString()` and only provide switch case statements for the enum values that you want to override. In your example, I get that they're all different but in actual use cases, I suspect that most of the single-word enum values will suffice and you'll only be providing overrides for multi-word enum values. – Scott Feb 05 '17 at 16:47
  • 1
    Make sure you follow up this design pattern with a unit test that dynamically gets the enum values to test and checks that newly inserted enums don't fall into the default unless you really meant for that to happen. Seen way too often these big case statements get missed somewhere in the app. – Denise Skidmore Jun 01 '18 at 14:23
  • 1
    This approach is better for strings, which should get translated. – testing Jun 26 '18 at 09:28
  • 2
    I'm torn between this and the attribute answer, but I've used this in the past successfully. It doesn't have the performance hit of Reflection, and you lose reflection when you take C# to Xamirin – Wesley Long Jan 31 '20 at 17:08
  • For a multilingual solution this ist the best way – marsh-wiggle May 16 '20 at 12:00
  • An extension method is also the best option if you don't have control over your enum declaration, like with a shared library or generated code. – RedMatt Feb 26 '21 at 14:33
  • 1
    This is a terrible idea. Every time someone changes the `enum`, they have to remember to go to the extension method and change that too (very likely to run into bugs in the future). This answer violates **Open/Closed Principle** and the fact that it has got so many upvotes is a proof that most of us programmers know pretty much nothing about OO principles. – Hooman Bahreini Dec 20 '21 at 03:57
  • I totally agree with you @HoomanBahreini. – Vishal modi Apr 10 '23 at 03:17
100

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.

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
annakata
  • 74,572
  • 17
  • 113
  • 180
28

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
{
    var 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");
}
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
  • 16
    I'm sorry, but thanks for trying to be helpful! Though because this is a Q&A site, answers *should* be an attempt to directly answer the question. And the question specifically states "*I don't need to be able to go from string to value again.*" Once again, thanks! – Jesse Apr 18 '13 at 00:31
  • 11
    Thanks for the positive criticism. It's always difficult being new to a site and learning about its culture and nuances. I am glad there are people like you who set the new guys straight. Once again, thanks for not dumping on the new guy. – Brian Richardson May 06 '13 at 20:49
  • 11
    @Jesse And 4 years later someone is happy to find bjrichardson code here! SO might be a Q&A site, but it doesn't mean the questions are dead after being answered. – John Oct 14 '17 at 21:02
  • @Jesse And 10 years later someone is happy to find Brian Richardson's code here! – Whitebrim Mar 06 '23 at 02:49
  • The great thing about places like stack overflow is that they preserve indicators of internet Nazis like Jesse up there, worried more about site conventions and keeping with the joneses than whether the content is actually helpful. – Chris Aug 23 '23 at 02:32
20

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

Noldorin
  • 144,213
  • 56
  • 264
  • 302
  • Although valid, I like the attribute way more. That way I can put my toSTring method in a seperate library, whilst putting the custom string representation with the enum itself – Boris Callens Jan 26 '09 at 11:13
  • 1
    Fair enough. I suppose one advantage of this method is that you can include an argument with the method specifying some state variable, and then change what string representation is returned depending on this. – Noldorin Jan 26 '09 at 11:24
  • 1
    Yes, it all depends on the scope of the method I guess. While the Attribute way is more generic, your solution is more localized.. It's all about needs in the end. – Boris Callens Jan 26 '09 at 11:34
  • 1
    You can put extension methods anywhere you want. You just have to reference it where you want to use them. –  Jan 26 '09 at 12:07
  • Yes, but this would mean that this one extention method should be rewritten every time you introduce a new enum you want to have a friendly name for. This would also mean that ALL your applications would carry around friendly names for ALL your other applications... – Boris Callens Jan 26 '09 at 14:40
  • CS1106 Extension method must be defined in a non-generic static class – shinzou Apr 09 '17 at 18:54
13

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.

Lemmy
  • 3,177
  • 3
  • 17
  • 16
  • 3
    toString is not safe in all cases - an enum with multiple entries with the same value (say for integer enums) will return the key of the first matching value, not the key of the item tested, this is why Enum.GetName is preferred – annakata Jan 26 '09 at 11:20
  • 4
    Well it was the easiest solution for his specific enum – Lemmy Jan 26 '09 at 11:38
12

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;
        }
    }
}
samus
  • 6,102
  • 6
  • 31
  • 69
  • 1
    +1-1=0 vote : This solution preserves the Enum syntax and does elegantly solve the problem without reflection or complex code, so +1 there. But it loses features of Enums themselves. So while IMO this is a good option, it doesn't answer the actual question and gets a -1. Net 0. Sorry we don't have a way to record that better in SO. – TonyG Oct 30 '17 at 21:45
  • @TonyG Fair enough. After missing a few questions on pluarlsight.com's .net skill assessment I started to realize how in depth C# enum's are, so it's probably a good idea to at least know of their capabilities before deciding which methodology to apply (especially for pervasive usage, refactoring can be a bit of a time;). – samus Oct 31 '17 at 15:55
8

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());
Milan Švec
  • 1,675
  • 17
  • 21
  • 2
    It's a mystery as to why this comment isn't the accepted one, or most upvoted - no reflection, no unnecessary attributes, ideal for simple situations where the enum is already nicely named. You could take this answer a step further and allow for adding spaces in-between capital letters before returning, 'My Enum'. – Vix Jul 14 '15 at 08:25
  • 14
    If the enum is already nicely named, there is no need for any extension method. Just use the existing ToString() method. `string result = "Result: " + ee;` – John Sep 11 '15 at 13:52
  • This should be the best answer. It works for any enum. You could even implement it using specific Enum just by changin the Enum type of the parameter to the Actual Enum on which to use it. – Juanu May 04 '16 at 15:18
  • 7
    This answer and all of the comments ignore the original request for an extended description. You guys totally missed the exercise which is to return something other than the default ToString value. I won't downvote all of the notes for this answer here but I sure want to. – TonyG Oct 30 '17 at 21:33
7

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)
Konrad Kokosa
  • 16,563
  • 2
  • 36
  • 58
  • 1
    It uses reflection as well and is not cached. https://github.com/Humanizr/Humanizer/blob/2e45bca3d4bfc8c9ff651a32490c8e7676558f14/src/Humanizer/EnumHumanizeExtensions.cs – Konrad Aug 14 '18 at 10:25
  • 1
    It will be as slow as the solution in the first answer by Ray – Konrad Aug 14 '18 at 10:27
6
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;
}
Massimiliano Kraus
  • 3,638
  • 5
  • 27
  • 47
Diogo Freitas
  • 69
  • 1
  • 1
  • 3
    It's always nice to write some text explaining why this should work and why the OP's wasn't. – phaberest Feb 23 '16 at 15:13
  • Just FYI, C# code conventions want local variables and method parameters with lower case initial letter. One exception is the `this` parameter in extension methods, that you can see called `This` in many examples in the web. Calling it like its type like you did (`Enum Enum`) make the code less readable. – Massimiliano Kraus Nov 30 '16 at 13:12
6

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.

Asher G.
  • 4,903
  • 5
  • 27
  • 30
  • This will lose the value of `Enums` when passing to methods as all methods signatures will require `string` so anything could be passed; introducing the need for additional validation and decreasing general readability. – CajunCoding Jul 26 '21 at 23:12
5

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();
Community
  • 1
  • 1
Joel MC
  • 107
  • 2
  • 7
  • 6
    The omission of a check for multiple Description attributes is on purpose. If the enum has two, and you're using to to generate a description, I'd like to think that is an exceptional circumstance. I think the actual bug is I don't do a Single() to have an exception thrown. Otherwise the whole method signature makes no sense. GetDescription()? Which description? An aggregate? – Ray Booysen Apr 14 '11 at 10:43
4

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
underscore
  • 752
  • 12
  • 9
3

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.

StefanDK
  • 173
  • 2
  • 10
3

Use Enum.GetName

From the above link...

using System;

public class GetNameTest {
    enum Colors { Red, Green, Blue, Yellow };
    enum Styles { Plaid, Striped, Tartan, Corduroy };

    public static void Main() {

        Console.WriteLine("The 4th value of the Colors Enum is {0}", Enum.GetName(typeof(Colors), 3));
        Console.WriteLine("The 4th value of the Styles Enum is {0}", Enum.GetName(typeof(Styles), 3));
    }
}
// The example displays the following output:
//       The 4th value of the Colors Enum is Yellow
//       The 4th value of the Styles Enum is Corduroy
J.M.H
  • 67
  • 3
2

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)

MarkusT
  • 269
  • 1
  • 3
  • 9
2

In case you just want to add a whitespace between the words, it is as simple as

string res = Regex.Replace(PublishStatusses.NotCompleted, "[A-Z]", " $0").Trim();
VladL
  • 12,769
  • 10
  • 63
  • 83
2

According to this documentation: https://learn.microsoft.com/pt-br/dotnet/api/system.enum.tostring?view=netframework-4.8

It is possible to just convert a enumerator to string using a format like this:

public enum Example
{
    Example1,
    Example2
}

Console.WriteLine(Example.Example1.ToString("g"));

//Outputs: "Example1"

You can see all the possible formats in this link: https://learn.microsoft.com/pt-br/dotnet/api/system.string?view=netframework-4.8

Emanuel Hiroshi
  • 318
  • 1
  • 7
  • 2
    Using the "g" (or "G") format string is the default behavior of an enum, as seen in the remarks on this page (https://learn.microsoft.com/en-us/dotnet/api/system.enum.tostring?view=netframework-4.8#System_Enum_ToString). – Mike Christiansen Mar 25 '21 at 14:17
  • 1
    This is a link to the format strings allowed for an Enum type. (https://learn.microsoft.com/en-us/dotnet/standard/base-types/enumeration-format-strings) – Mike Christiansen Mar 25 '21 at 14:17
2

I'm 7 years late for the party :-) But I'm sure this topic is visited frequently.
So I wanted to add a little sugar to the coffee:

What about the "F" format string specifier?

PublishStatusses[] ps = Enum.GetValues<PublishStatusses>();
ps.ToList().ForEach(c => Console.Write($"{c:F} "));

There is no need for any explicit function call.

In fact there isn't even need for any format specifier. In case of a variable assignment to a string, ToString() does the work:

string foo = PublishStatusses.Error.ToString(); // or ToString("F")

And if it is about to insert spaces between words of a CamelCase string, you can just use a regular expression:

Regex.Replace(foo, "(\\B[A-Z])", " $1")
A.B.
  • 2,374
  • 3
  • 24
  • 40
1

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();
    }   
Richard Anthony Hein
  • 10,550
  • 3
  • 42
  • 62
1

Just use a static class that simulate an enum:

public static class PublishStatusses{
   public const string NotCompleted = "Not Completed";
   public const string Completed = "Completed";
   public const string Error = "Error"
};

And to access the values, just use like an enum:

PublishStatusses.NotCompleted;
Rodrigo
  • 188
  • 1
  • 8
0

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);
    }
Lukich
  • 1
0

In case you use Newtonsoft anyway, you can do it as follows:

// Enum

[Newtonsoft.Json.JsonConverter(typeof(StringEnumConverter))]
public enum MyEnum
{
   [EnumMember(Value = "User friendly value")]
   SomeValue=0,
}

// Usage as extension method

public static string ToDefaultString(this Enum enumValue)
{
   return JsonConvert.SerializeObject(enumValue).Replace("\"", "");
}


Kiechlus
  • 1,167
  • 12
  • 21
-1

I use a generic class to store the enum/description pairs and a nested helper class to get the description.

The enum:

enum Status { Success, Fail, Pending }

The generic class:

Note: Since a generic class cannot be constrained by an enum I am constraining by struct instead and checking for enum in the constructor.

public class EnumX<T> where T : struct
{
    public T Code { get; set; }
    public string Description { get; set; }

    public EnumX(T code, string desc)
    {
        if (!typeof(T).IsEnum) throw new NotImplementedException();

        Code = code;
        Description = desc;
    }

    public class Helper
    {
        private List<EnumX<T>> codes;

        public Helper(List<EnumX<T>> codes)
        {
            this.codes = codes;
        }

        public string GetDescription(T code)
        {
            EnumX<T> e = codes.Where(c => c.Code.Equals(code)).FirstOrDefault();
            return e is null ? "Undefined" : e.Description;
        }
    }
}

Usage:

EnumX<Status>.Helper StatusCodes = new EnumX<Status>.Helper(new List<EnumX<Status>>()
        {
            new EnumX<Status>(Status.Success,"Operation was successful"),
            new EnumX<Status>(Status.Fail,"Operation failed"),
            new EnumX<Status>(Status.Pending,"Operation not complete. Please wait...")
        });

        Console.WriteLine(StatusCodes.GetDescription(Status.Pending));
zisha
  • 11,352
  • 1
  • 12
  • 3
-2

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)
    {

    }
-3

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.

Kevin Pang
  • 41,172
  • 38
  • 121
  • 173