597

I know the following is not possible because the Enumeration's type has to be an int

enum GroupTypes
{
    TheGroup = "OEM",
    TheOtherGroup = "CMB"
}

From my database I get a field with incomprehensive codes (the OEM and CMBs). I would want to make this field into an enum or something else understandable. Because if the target is readability, the solution should be terse.

What other options do I have?

Chad
  • 1,531
  • 3
  • 20
  • 46
Boris Callens
  • 90,659
  • 85
  • 207
  • 305
  • 1
    possible duplicate of [Enum ToString](http://stackoverflow.com/questions/479410/enum-tostring) – nawfal Jun 08 '13 at 22:41
  • 23
    I'm not sure why most of the answers don't just use "const string" and instead they're making custom classes. – CTS_AE May 15 '15 at 22:23
  • 2
    You may not be able to use strings, but you can use chars just fine. That's an option if you can use single-letter values. – T. Sar Jun 14 '17 at 15:30
  • 1
    Genuinely confused as to why the solution proposed above by CTS_AE is not even in the top three answers. – Sinjai Sep 05 '17 at 21:57
  • @Sinjai Explicit grouping of related values will outweigh the penalty of an imperceptible performance loss, especially in an API or reusable component. – person27 Aug 26 '18 at 04:18
  • I am confused. While, creating a custom class as suggested in the proposed answer will work, I am of the opinion that a `const string` seems the indeal candidate for this problem. I would keep it simple. – Sau001 Jul 17 '20 at 13:19
  • 5
    @Sau001 / CTS_AE - wanted to put this here since these comments are at the top and it takes a bit of scrolling to find the response. See Pharap's comment on this answer: https://stackoverflow.com/a/5674697/5948647. Static classes cannot be used as method parameter types so you cannot enforce the use of one of your pre-defined constant strings on methods. – Robin Zimmerman Aug 13 '20 at 20:43

39 Answers39

656

I like to use properties in a class instead of methods, since they look more enum-like.

Here's an example for a Logger:

public class LogCategory
{
    private LogCategory(string value) { Value = value; }

    public string Value { get; private set; }

    public static LogCategory Trace   { get { return new LogCategory("Trace"); } }
    public static LogCategory Debug   { get { return new LogCategory("Debug"); } }
    public static LogCategory Info    { get { return new LogCategory("Info"); } }
    public static LogCategory Warning { get { return new LogCategory("Warning"); } }
    public static LogCategory Error   { get { return new LogCategory("Error"); } }

    public override string ToString()
    {
        return Value;
    }
}

Pass in type-safe string values as a parameter:

public static void Write(string message, LogCategory logCategory)
{
    var log = new LogEntry { Message = message };
    Logger.Write(log, logCategory.Value);
}

Usage:

Logger.Write("This is almost like an enum.", LogCategory.Info);
Pawel Cioch
  • 2,895
  • 1
  • 30
  • 29
Even Mien
  • 44,393
  • 43
  • 115
  • 119
  • 4
    Only down side I can come up with is that it would be a tiny bit slower, but this would in most cases be neglectable. And it wouldn't have the exact same behaviour in the editor. E.G.: switching over this one wouldn't automatically fill in a case for each possibility. Other than those minor points, I think this is probably a rather simple solution. – Boris Callens Aug 28 '09 at 07:06
  • 3
    And it's easy to use Dictionary as a switch. :) – Arnis Lapsa Dec 05 '09 at 16:06
  • 4
    @ArnisL. It's not enough to work as key, you need to override Equals() and GetHashCode(), and you want to make the Value property setter private. Still, it's not an enum. – Dave Van den Eynde Feb 01 '13 at 07:41
  • 45
    For my own use, I expanded upon this concept, overriding the `ToString` method to return `Value`. And then provided implicit cast operators to and from a string. `public static implicit operator String(LogCategory category) { return Value; }`. – Zarepheth Jan 24 '14 at 20:17
  • can it be iterated like this with enums? enum LogCategory { Trace, Debug, Info, Warning, Error } foreach (LogCategory logcat in Enum.GetValues(typeof(LogCategory))) – jrivam Sep 10 '14 at 03:00
  • 16
    What about using this in switch cases? – David Feb 03 '15 at 10:56
  • 1
    why not just enum.ToString("F") ? – GL_monk-342435 Jun 04 '15 at 21:04
  • 6
    Brilliant decision. I would make the `Value` setter private, thou. – bohdan_trotsenko Nov 11 '15 at 09:17
  • 4
    Downside is that no use as default value in function parameters: public string GetLogs(LogCategory cat = LogCategory.Trace) Will return an error "default parameter value must be a compile-time constant". – Miro J. Nov 19 '15 at 19:30
  • 1
    public override string ToString() { return Value; } Logger.Information("Category is: {category}", LogCategory.Trace); // with Serilog logger – pashute May 04 '16 at 10:17
  • 1
    Honestly, after so many years passing your example almost cries for `CallerMemberNameAttribute` or `nameof`(...) – Lorenz Lo Sauer Sep 03 '16 at 13:43
  • 18
    With c# 6 now, you can make this a lot neater, `public static string Trace => "Trace";`. – JonathanPeel Dec 02 '16 at 08:57
  • now VS intellisense won't Auto fill variables when comparing or initializing like what it does with enumerations .... – mahmoud nezar sarhan Sep 14 '17 at 14:37
  • 2
    @ghanashyaml Because that doesn't work for strings with spaces or if you want different capitalization than the enum. – VSO May 15 '18 at 19:24
  • 1
    I used `struct` instead of `class` in order to pass `Assert.Equal(LogCategory.Info, LogCategory.Info);` Also, used a `private set;` for `Value`. – Myobis Jul 23 '18 at 09:21
  • I refactored to something simpler: `public class LogCategory { public static string Trace { get { return "Trace"; } } public static string Debug { get { return "Debug"; } } public static string Info { get { return "Info"; } } public static string Warning { get { return "Warning"; } } public static string Error { get { return "Error"; } } }` – Roger Sampaio Mar 21 '19 at 14:40
  • 2
    @RogerSampaio Since you are returning strings, you can't use the class as a parameter type – Oylex Apr 18 '19 at 18:39
  • 2
    I would like to note that this implementation will consume more memory (since each time you access a property you create a new object), especially if you're using the properties a lot. – Никола Караклић May 07 '19 at 08:43
  • 3
    @НиколаКараклић you're absolutely right. These static properties should have been auto initialized. – Loudenvier Aug 21 '19 at 19:49
  • Works, but not as clean as @darasd's solution below. – Chad Sep 18 '19 at 20:13
  • 1
    Instead of allocate the object every time you access the property, how about this `private static GroupTypes _theGroup; public static GroupTypes TheGroup { get { if(_theGroup == null) { _theGroup = new GroupTypes("OEM"); } return _theGroup; } }` as the value itself is holding in static private and only initialize if this private property is null, otherwise return the initialized value. – Deszolate_C Jan 03 '20 at 05:04
  • Also doesn't lend itself to usage in EntityFramework.. ended up working around it: [Column("Status")] public string StatusInternal { get; private set; } [NotMapped] public virtual UserStatus Status { get { ... } set { StatusInternal = value.Value; } } – Liam Fleming Jan 07 '20 at 02:49
  • 1
    I also overrode Equals (and related HashCode and operators), because I got caught comparing this as a reference type. (I used VS Quick Actions - first time that stuff was ever useful to me!) – goodeye Mar 26 '20 at 23:15
  • 3
    How can this solution be used to deserialize a json with a property of LogCategoryType? – Vityka Apr 07 '20 at 19:16
  • 2
    Even better - Use a static constructor: ```public class GroupType { static GroupType() { TheGroup = new GroupType("OEM"); TheOtherGroup = new GroupType("CMB"); } private GroupType(string name) { Name = name; } public static GroupType TheGroup { get; } public static GroupType TheOtherGroup { get; } public string Name { get; } }``` preserves equality without overriding `.Equals` and maintains a singleton of each value avoiding unnecessary instantiation and memory overhead. – Robin Zimmerman Aug 13 '20 at 21:11
  • 1
    Lol, I came to this post exactly for this example - writing a "string" enum for various logger tags! – fullStackChris Feb 25 '21 at 18:21
  • You could look into type-safe-enum. A concept I’ve seen in Java more so than C#. – Martin Nov 05 '22 at 14:22
  • This is best to enforce type safety – Pawel Cioch Nov 23 '22 at 18:17
  • Is there a name for this pattern? – Patrick Szalapski Feb 23 '23 at 15:51
  • 1
    @Evan Mien, wouldn't it be better to make static properties indeed get-only, but with a proper stored value? `public static LogCategory Trace { get; } = new("Trace");` That way every instance of Trace (and the others) would be the same reference. Also, this addresses the concern above from @Никола Караклић – Patrick Szalapski Feb 23 '23 at 15:53
  • Why aren't "enum values" just static readonly fields? Why create a new instance on every get? – blazkovicz Jun 12 '23 at 08:12
276

You could also use the extension model:

public enum MyEnum
{
    [Description("String 1")]
    V1= 1,
    [Description("String 2")]
    V2= 2
} 

Your Extension Class

public static class MyEnumExtensions
{
    public static string ToDescriptionString(this MyEnum val)
    {
        DescriptionAttribute[] attributes = (DescriptionAttribute[])val
           .GetType()
           .GetField(val.ToString())
           .GetCustomAttributes(typeof(DescriptionAttribute), false);
        return attributes.Length > 0 ? attributes[0].Description : string.Empty;
    }
} 

usage:

MyEnum myLocal = MyEnum.V1;
print(myLocal.ToDescriptionString());
kmote
  • 16,095
  • 11
  • 68
  • 91
Glennular
  • 17,827
  • 9
  • 58
  • 77
  • Getting an error `'New.MyEnum' does not contain a definition for 'ToDescriptionString' and no extension method 'ToDescriptionString' accepting a first argument of type 'New.MyEnum' could be found (are you missing a using directive or an assembly reference?)` please help. Here is my code file http://bobdn.com/Temp/enumsTest.txt – shashwat Sep 11 '12 at 10:46
  • 3
    See also http://stackoverflow.com/questions/4367723/get-enum-from-description-attribute for another extension and from string to enum by way of description. – Dave Dec 09 '13 at 21:41
  • 42
    I can't help thinking that reflecting the enum every time you want do display the text sounds kind of painful from a performance perspective! – Liath Sep 01 '14 at 15:03
  • 8
    @Liath - The ` .ToString() ` already uses reflection, so you're not really losing anything with this approach, and gaining readibility – James King Mar 28 '16 at 18:24
  • 1
    Could you make the Extension generic so it applied automatically to all Enums? – erosebe May 25 '17 at 21:06
  • 7
    To make generic, use `public static string ToDescriptionString(this Enum ...` i.e. without explicitly typing to `MyEnum`. – LeeCambl Sep 15 '18 at 13:29
  • 1
    This requires `using System.ComponentModel;` – Cody G Jun 06 '19 at 12:23
  • I think this is sufficient for a generic version. However, after further _reflection_ I think I will opt to not use reflection for my current single enum use case due to performance concerns. public static string ToDescriptionString(this T val) where T : Enum – yourbuddypal Jul 10 '20 at 15:51
  • Safe version would be: DescriptionAttribute[] attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString())?.GetCustomAttributes(typeof(DescriptionAttribute), false); return attributes is {Length: > 0} ? attributes[0].Description : string.Empty; – Matěj Štágl Aug 21 '21 at 13:07
175

How about using a static class with constants?

static class GroupTypes
{
  public const string TheGroup = "OEM";
  public const string TheOtherGroup = "CMB";
}

void DoSomething(string groupType)
{
  if(groupType == GroupTypes.TheGroup)
  {
    // Be nice
  }  
  else if (groupType == GroupTypes.TheOtherGroup)
  {
    // Continue to be nice
  }
  else
  {
    // unexpected, throw exception?
  }
}
rpattabi
  • 9,984
  • 5
  • 45
  • 53
  • 11
    Agreed. I'm having trouble seeing the purpose behind the more complex solutions, except maybe to be able to switch over the resulting "enum". – fakeleft Dec 16 '11 at 12:55
  • 1
    @fakeleft you cannot use a static class type with a generic type (template), and maybe other limitations, I think that is why people prefer the "more complex" solutions. – eselk Nov 24 '15 at 16:49
  • 3
    The constants need to be internal or public for this to work though – arviman Nov 30 '15 at 07:05
  • 79
    Static types cannot be used as parameters. – Pedro Moreira Nov 22 '16 at 17:31
  • 1
    I prefer this approach, with all constant grouping class, under one class. So that I can access them like Constants.GroupTypes.TheGroup – zak Jul 31 '18 at 11:40
  • 10
    As @PedroMoreira points out, you can't pass `GroupTypes` as an argument type because it's a static class. That's the problem that Even Mien's answer solves. In this case you'd instead have to have `void DoSomething(string groupType)`, which then means that `groupType` could have **any string value whatsoever**, even values that you aren't expecting, which means you have to be prepared for those invalid types and decide how to handle them (e.g. by throwing an exception). Even Mien's answer solves that by limiting the number of valid inputs to the options defined by the `LogCategory` class. – Pharap Jun 19 '19 at 06:01
  • 1
    @fakeleft It's possible to switch on `string`s too. (Also, see my above comment about the limitations of this approach.) – Pharap Jun 19 '19 at 06:02
  • 1
    How does this answer have so many upvotes when it isn't even valid code? This doesn't work. – codeConcussion Oct 18 '19 at 19:28
  • 1
    I don't code in c# anymore, so after revising the answer based on the comments here, I have made it a wiki. Feel free to improve it. – rpattabi Oct 20 '19 at 11:42
  • 1
    DoSomething("I am Junior developer and want to use InetlliSense") will compile and fail in release. this method requires dozens of extra unit tests. – Andrew Matiuk May 18 '20 at 11:09
  • This is indeed easy but you can't type arguments with this. Only as string. If what you want is to constrain the type to one of these values then this won't work. – justin.m.chase May 14 '23 at 00:49
55

I used a structure as alluded to in a previous answer, but did away with any complexity. To me, this was most like creating an enumeration of strings. It is used in the same manner that an enumeration is used.

    struct ViewTypes
    {
        public const string View1 = "Whatever string you like";
        public const string View2 = "another string";
    }

Example use:

   switch( some_string_variable )
   {
      case ViewTypes.View1: /* do something */ break;
      case ViewTypes.View2: /* do something else */ break;
   }
Scott
  • 789
  • 6
  • 7
  • 2
    Out of all the answers given, I think this is the best solution. Almost all of the answers are trying to shoehorn a solution, but they add more complexity than there should be. Sometimes it's simply better to use a different solution or data structure instead of forcing something to work the way you think it should. I personally use structs like this instead of enums and it makes my code much more readable and maintainable. – Halcyon Aug 12 '21 at 16:25
  • simple and effective! – Canada Wan Sep 24 '21 at 15:22
47

Try adding constants to a static class. You don't end up with a Type, but you will have readable, organised constants:

public static class GroupTypes {

    public const string TheGroup = "OEM";
    public const string TheOtherGroup = "CMB";

}
Chad
  • 1,531
  • 3
  • 20
  • 46
darasd
  • 2,899
  • 3
  • 26
  • 39
  • 4
    Difficult to go from the code back to the descriptive name. You would have to use reflection over all the const fields to search for a match. – andleer Mar 10 '09 at 15:43
  • 1
    @andleer I don't understand your concern. This is the solution I use. – VSO May 15 '18 at 19:37
  • Yeah this is actually precisely what I wanted. And this is the most concise/elegant solution I see, just as if I were defining an enumeration w/ int values - but with string values instead. 100% perfect. – Chad Sep 18 '19 at 20:08
  • 8
    The problem with this is that it doesn't work as an Enum in the sense that we won't have a separate type with a finite list of values. A function expecting these could be used with free-form strings which is error prone. – Juan Martinez Feb 21 '20 at 00:55
44

You can do it very easily actually. Use the following code.

enum GroupTypes
{
   OEM,
   CMB
};

Then when you want to get the string value of each enum element just use the following line of code.

String oemString = Enum.GetName(typeof(GroupTypes), GroupTypes.OEM);

I've used this method successfully in the past, and I've also used a constants class to hold string constants, both work out pretty well, but I tend to prefer this.

Arthur C
  • 618
  • 5
  • 5
  • I was thinking the same thing, but there must be some catch to this... Otherwise I would suspect more people would suggest this (Maybe I'm just paranoid). – Matthijs Wessels Jan 11 '10 at 14:14
  • The only catch I'm aware of to this is that I believe it uses reflection to figure out the string. As a result if I'm just after a solution to keep track of a constant string, then I typically will use a class to store the majority of my constant strings. However if I have situation where an Enum is the right solution (regardless of getting a descriptive string about my Enum elements), then rather than have an extra string floating around somewhere to manage I just use the enum value as described. – Arthur C Jan 11 '10 at 16:24
  • +1 This IS the best and easiest answer, and also has high votes [here](http://stackoverflow.com/questions/483794/convert-enum-to-string#4412730) to prove it. The only time it is better to use [the extension model](http://stackoverflow.com/questions/630803/associating-enums-with-strings-in-c-sharp#630900) is when you need spaces in the text (more details [here](http://weblogs.asp.net/grantbarrington/archive/2009/01/19/enumhelper-getting-a-friendly-description-from-an-enum.aspx)). – SharpC Aug 07 '13 at 19:15
  • 32
    No, this is just getting an enum value's name, not assigning a string to an enum value. The goal of the OP is to have a string different from the enum value eg : TheGroup = "OEM", TheOtherGroup = "CMB". – Tim Autin Nov 03 '14 at 10:29
  • 3
    I agree with @Tim's comment, this is *not* what the OP is trying to do. If you're wondering what a use case of this is, consider a situation where a device takes strings as commands, but there needs to be a "human readable" version of the command as well. I had need of this to associate something like "Update Firmware" with the command "UPDATEFW". – JYelton Jan 02 '15 at 20:19
  • Is there some reason not to just use `GroupTypes.OEM.ToString()` in this case? – NetMage Jun 24 '19 at 22:12
  • besides this won't work for strings that are not valid identifiers like "123" or `some string. 123` – phuclv Sep 27 '19 at 23:00
34

You can add attributes to the items in the enumeration and then use reflection to get the values from the attributes.

You would have to use the "field" specifier to apply the attributes, like so:

enum GroupTypes
{
    [field:Description("OEM")]
    TheGroup,

    [field:Description("CMB")]
    TheOtherGroup
}

You would then reflect on the static fields of the type of the enum (in this case GroupTypes) and get the DescriptionAttribute for the value you were looking for using reflection:

public static DescriptionAttribute GetEnumDescriptionAttribute<T>(
    this T value) where T : struct
{
    // The type of the enum, it will be reused.
    Type type = typeof(T);

    // If T is not an enum, get out.
    if (!type.IsEnum) 
        throw new InvalidOperationException(
            "The type parameter T must be an enum type.");

    // If the value isn't defined throw an exception.
    if (!Enum.IsDefined(type, value))
        throw new InvalidEnumArgumentException(
            "value", Convert.ToInt32(value), type);

    // Get the static field for the value.
    FieldInfo fi = type.GetField(value.ToString(), 
        BindingFlags.Static | BindingFlags.Public);

    // Get the description attribute, if there is one.
    return fi.GetCustomAttributes(typeof(DescriptionAttribute), true).
        Cast<DescriptionAttribute>().SingleOrDefault();
}

I opted to return the DescriptionAttribute itself above, in the event that you want to be able to determine whether or not the attribute is even applied.

casperOne
  • 73,706
  • 19
  • 184
  • 253
  • Although I will remember this for more complex situations, it is rather complex for a situation with the complexity level of what I stated in the OP – Boris Callens Sep 24 '12 at 10:15
16

Use a class.

Edit: Better example

class StarshipType
{
    private string _Name;
    private static List<StarshipType> _StarshipTypes = new List<StarshipType>();

    public static readonly StarshipType Ultralight = new StarshipType("Ultralight");
    public static readonly StarshipType Light = new StarshipType("Light");
    public static readonly StarshipType Mediumweight = new StarshipType("Mediumweight");
    public static readonly StarshipType Heavy = new StarshipType("Heavy");
    public static readonly StarshipType Superheavy = new StarshipType("Superheavy");

    public string Name
    {
        get { return _Name; }
        private set { _Name = value; }
    }

    public static IList<StarshipType> StarshipTypes
    {
        get { return _StarshipTypes; }
    }

    private StarshipType(string name, int systemRatio)
    {
        Name = name;
        _StarshipTypes.Add(this);
    }

    public static StarshipType Parse(string toParse)
    {
        foreach (StarshipType s in StarshipTypes)
        {
            if (toParse == s.Name)
                return s;
        }
        throw new FormatException("Could not parse string.");
    }
}
Kevin Doyon
  • 3,464
  • 2
  • 33
  • 38
C. Ross
  • 31,137
  • 42
  • 147
  • 238
  • 1
    Difficult to go from the code back to the descriptive name. You would have to use reflection over all the const fields to search for a match. – andleer Mar 10 '09 at 15:42
  • 1
    I see your point. I will upload a version that acutally works later, but I admit it's pretty heavy. – C. Ross Mar 11 '09 at 13:28
  • My version based on C. Ross's solution https://stackoverflow.com/a/48441114/3862615 – Roman M Jan 25 '18 at 10:54
15

Create a second enum, for your DB containing the following:

enum DBGroupTypes
{
    OEM = 0,
    CMB = 1
}

Now, you can use Enum.Parse to retrieve the correct DBGroupTypes value from the strings "OEM" and "CMB". You can then convert those to int and retrieve the correct values from the right enumeration you want to use further in your model.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Dave Van den Eynde
  • 17,020
  • 7
  • 59
  • 90
12

Another way to deal with the problem, is to have a enum and a array of strings that will map the enum values with the list of strings:

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

string[] GroupTypesStr = {
    "OEM",
    "CMB"
};

you may use it something like this:

Log.Write(GroupTypesStr[(int)GroupTypes.TheOtherGroup]);

It will prompt CMB

PROS:

  1. Easy and clean code.
  2. High Performance (specially in comparison with those approaches that uses classes)

CONS:

  1. Prone to mess up the list when editing it, but it will be okay for a short list.
Luis Orantes
  • 407
  • 5
  • 13
11

Here is the extension method that I used to get the enum value as string. First here is the enum.

public enum DatabaseEnvironment
{
    [Description("AzamSharpBlogDevDatabase")]
    Development = 1, 
    [Description("AzamSharpBlogQADatabase")]
    QualityAssurance = 2, 
    [Description("AzamSharpBlogTestDatabase")] 
    Test = 3
}

The Description attribute came from System.ComponentModel.

And here is my extension method:

public static string GetValueAsString(this DatabaseEnvironment environment) 
{
    // get the field 
    var field = environment.GetType().GetField(environment.ToString());
    var customAttributes = field.GetCustomAttributes(typeof (DescriptionAttribute), false);

    if(customAttributes.Length > 0)
    {
        return (customAttributes[0] as DescriptionAttribute).Description;  
    }
    else
    {
        return environment.ToString(); 
    }
}

Now, you can access the enum as string value using the following code:

[TestFixture]
public class when_getting_value_of_enum
{
    [Test]
    public void should_get_the_value_as_string()
    {
        Assert.AreEqual("AzamSharpBlogTestDatabase",DatabaseEnvironment.Test.GetValueAsString());  
    }
}
Shoaib Shakeel
  • 1,447
  • 18
  • 24
azamsharp
  • 19,710
  • 36
  • 144
  • 222
8

New in .Net Core 3.0/C# 8.0 (if your work environment allows you to upgrade your project) is a short-hand switch statement that looks somewhat enum-ish. At the end of the day it's the same old boring switch statement we've been using for years.

Only real difference here is that the switch statement got a new suit.

public static RGBColor FromRainbow(Rainbow colorBand) =>
colorBand switch
{
    Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
    Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
    Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
    Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
    Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
    Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
    Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
    _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
};

You'll notice that the code above which I copied from here, is actually using an enum as a param.

It's not exactly what you want (and trust me, I've wanted something of similar to what the OP is requesting for a long time), but I actually feel like this is somewhat of an olive branch from MS. JMO.

Hope it helps someone!

user3180664
  • 185
  • 2
  • 6
7

Taken from @EvenMien and added in some of the comments. (Also for my own use case)

public struct AgentAction
{
    private AgentAction(string value) { Value = value; }

    public string Value { get; private set; }

    public override string ToString() { return this.Value; }
    
    public static AgentAction Login = new AgentAction("Login");
    public static AgentAction Logout = new AgentAction("Logout");

    public static implicit operator string(AgentAction action) { return action.ToString(); }
}
David Guerra
  • 79
  • 2
  • 6
moldypenguins
  • 450
  • 5
  • 8
  • 1
    with the string operator there is no more need for a public Value. I use `private readonly string Value;` instead – 5andr0 Sep 16 '21 at 22:14
7

Why not just use the same enum, but just call .ToString()?

using System;

public class EnumSample
{
    enum Holidays
    {
        Christmas = 1,
        Easter = 2
    };

    public static void Main()
    {
        Enum myHolidays = Holidays.Christmas;
        Console.WriteLine("The value of this instance is '{0}'", myHolidays.ToString());
    }
}
  • 2
    This is the best answer when the display text has no spaces. You can use helper functions that convert pascal case in to words with spaces if you need to as well. – Tolga Jun 24 '21 at 04:03
  • As mentioned before this is not the goal. Instead the question was: Holidays.Christmas = "Xmas" instead of 1 – somedotnetguy Jun 15 '22 at 10:49
  • what do you do when you need to return "Option A (Fund charge 1xperc)"? ;) – Ray Brennan Oct 10 '22 at 08:57
5

Have you considered a lookup table using a Dictionary?

enum GroupTypes
{
    TheGroup,
    TheOtherGroup
}

Dictionary<string, GroupTypes> GroupTypeLookup = new Dictionary<string, GroupTypes>();
// initialize lookup table:
GroupTypeLookup.Add("OEM", TheGroup);
GroupTypeLookup.Add("CMB", TheOtherGroup);

You can then use GroupTypeLookup.TryGetValue() to look up a string when you read it.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
4
public class DataType
{
    private readonly string value;
    private static readonly Dictionary<string, DataType> predefinedValues;

    public static readonly DataType Json = new DataType("json");
    public static readonly DataType Xml = new DataType("xml");
    public static readonly DataType Text = new DataType("text");
    public static readonly DataType Html = new DataType("html");
    public static readonly DataType Binary = new DataType("binary");

    static DataType()
    {
        predefinedValues = new Dictionary<string, DataType>();
        predefinedValues.Add(Json.Value, Json);
        predefinedValues.Add(Xml.Value, Xml);
        predefinedValues.Add(Text.Value, Text);
        predefinedValues.Add(Html.Value, Html);
        predefinedValues.Add(Binary.Value, Binary);
    }

    private DataType(string value)
    {
        this.value = value;
    }

    public static DataType Parse(string value)
    {
        var exception = new FormatException($"Invalid value for type {nameof(DataType)}");
        if (string.IsNullOrEmpty(value))
            throw exception;

        string key = value.ToLower();
        if (!predefinedValues.ContainsKey(key))
            throw exception;

        return predefinedValues[key];
    }

    public string Value
    {
        get { return value; }
    }
}
Roman M
  • 450
  • 1
  • 5
  • 12
4

I would just create a dictionary and use the code as the key.

Edit: To address the comment about doing a reverse lookup (finding the key), this would not be terribly efficient. If this is necessary, I would write a new class to handle it.

jhale
  • 1,790
  • 3
  • 14
  • 21
4

Here is my take on this, using C# 9.0 syntax to keep it clean. I define a base class for the enums:

public class StringEnum
{
    protected StringEnum(string value) { Value = value; }
    public string Value { get; }
    public override string ToString() => Value;
}

Creating new enum style types is then easy and compact:

public class GroupTypes : StringEnum
{ 
    private GroupTypes(string value) : base(value) {}

    public static readonly GroupTypes TheGroup = new("OEM");
    public static readonly GroupTypes TheOtherGroup = new("CMB");
}

Use it like this:

void Example(GroupTypes groupType)
{
    Console.WriteLine(groupType); // Will print "OEM" or "CMB"
    if (groupType == GroupTypes.TheGroup) { ... }
}

You can also add more functionality to StringEnum, which will then be available for all your subclasses (e. g., implementing IComparable and overriding Equals and GetHashCode)

Rolf Staflin
  • 2,092
  • 2
  • 22
  • 19
  • Shouldn't you validate that value is one of the valid group types in the constructor? – justin.m.chase May 14 '23 at 00:53
  • @justin.m.chase The constructor in `StringEnum` is protected, so it can only be called from subclasses and in the subclasses you make the constructor private. In `GroupTypes` above, there's no way to create new types from the "outside" – instead you always use the static fields `TheGroup` and `TheOtherGroup`. – Rolf Staflin May 14 '23 at 07:36
3

C# doesn't support enumerated strings, but for most situations you can use a List or Dictionary to get the desired effect.

E.g. To print pass/fail results:

List<string> PassFail = new List<string> { "FAIL", "PASS" };
bool result = true;
Console.WriteLine("Test1: " + PassFail[result.GetHashCode()]);
user1441126
  • 141
  • 5
3

This is a way to use it as a strongly typed parameter or as a string :

public class ClassLikeEnum
{
    public string Value
    {
        get;
        private set;
    }

    ClassLikeEnum(string value) 
    {
        Value = value;
    }

    public static implicit operator string(ClassLikeEnum c)
    {
        return c.Value;
    }

    public static readonly ClassLikeEnum C1 = new ClassLikeEnum("RandomString1");
    public static readonly ClassLikeEnum C2 = new ClassLikeEnum("RandomString2");
}
Nicolas Bodin
  • 1,431
  • 1
  • 20
  • 25
3

A small tweak to Glennular Extension method, so you could use the extension on other things than just ENUM's;

using System;
using System.ComponentModel;
namespace Extensions {
    public static class T_Extensions {
        /// <summary>
        /// Gets the Description Attribute Value
        /// </summary>
        /// <typeparam name="T">Entity Type</typeparam>
        /// <param name="val">Variable</param>
        /// <returns>The value of the Description Attribute or an Empty String</returns>
        public static string Description<T>(this T t) {
            DescriptionAttribute[] attributes = (DescriptionAttribute[])t.GetType().GetField(t.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return attributes.Length > 0 ? attributes[0].Description : string.Empty;
        }
    }
}

Or Using Linq

using System;
using System.ComponentModel;
using System.Linq;

namespace Extensions {


public static class T_Extensions {
        public static string Description<T>(this T t) =>
            ((DescriptionAttribute[])t
            ?.GetType()
            ?.GetField(t?.ToString())
            ?.GetCustomAttributes(typeof(DescriptionAttribute), false))
            ?.Select(a => a?.Description)
            ?.FirstOrDefault() 
            ?? string.Empty;  
    }
}
Mike
  • 1,525
  • 1
  • 14
  • 11
3

Following the answer of @Even Mien I have tried to go a bit further and make it Generic, I seem to be almost there but one case still resist and I probably can simplify my code a bit.
I post it here if anyone see how I could improve and especially make it works as I can't assign it from a string

So Far I have the following results:

        Console.WriteLine(TestEnum.Test1);//displays "TEST1"

        bool test = "TEST1" == TestEnum.Test1; //true

        var test2 = TestEnum.Test1; //is TestEnum and has value

        string test3 = TestEnum.Test1; //test3 = "TEST1"

        var test4 = TestEnum.Test1 == TestEnum.Test2; //false
         EnumType<TestEnum> test5 = "TEST1"; //works fine

        //TestEnum test5 = "string"; DOESN'T compile .... :(:(

Where the magics happens :

public abstract  class EnumType<T>  where T : EnumType<T>   
{

    public  string Value { get; set; }

    protected EnumType(string value)
    {
        Value = value;
    }


    public static implicit operator EnumType<T>(string s)
    {
        if (All.Any(dt => dt.Value == s))
        {
            Type t = typeof(T);

            ConstructorInfo ci = t.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,null, new Type[] { typeof(string) }, null);

            return (T)ci.Invoke(new object[] {s});
        }
        else
        {
            return null;
        }
    }

    public static implicit operator string(EnumType<T> dt)
    {
        return dt?.Value;
    }


    public static bool operator ==(EnumType<T> ct1, EnumType<T> ct2)
    {
        return (string)ct1 == (string)ct2;
    }

    public static bool operator !=(EnumType<T> ct1, EnumType<T> ct2)
    {
        return !(ct1 == ct2);
    }


    public override bool Equals(object obj)
    {
        try
        {
            return (string)obj == Value;
        }
        catch
        {
            return false;
        }
    }

    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }

    public static IEnumerable<T> All
     => typeof(T).GetProperties()
       .Where(p => p.PropertyType == typeof(T))
       .Select(x => (T)x.GetValue(null, null));



}

I only then have to declare this for my enums:

public class TestEnum : EnumType<TestEnum> 
{

    private TestEnum(string value) : base(value)
    {}

    public static TestEnum Test1 { get { return new TestEnum("TEST1"); } }
    public static TestEnum Test2 { get { return new TestEnum("TEST2"); } }
}
Lomithrani
  • 2,033
  • 3
  • 18
  • 24
  • Thank you for this great job, I was looking for such approach for a long time. I think you should get 1000 points for this – user3492977 Mar 24 '20 at 17:52
  • Oh thank you for this comment, and thank you for reminding me of this, I hadn't used c# for two years when I wrote this bit of code , I should get back to it soon ! – Lomithrani Mar 25 '20 at 10:11
  • @user3492977 I finally got back to it and made it fully functionnal, I am still doubtful though if it's a great idea or a useless thing :D https://stackoverflow.com/questions/62043138/associating-enums-with-string-c-sharp – Lomithrani May 27 '20 at 12:32
3

My first question - Do you have access to the Database itself? This should be normalized in the database, ideally, otherwise, any solution is going to be prone to error. In my experience, data fields full of "OEM" and "CMB" tend to wind up having things like "oem " and other 'crap data' mixed in over time.... If you can normalize it, you could use the key in the table containing the elements as your Enum, and you're done, with a much cleaner structure.

If that's not available, I'd make your Enum, and make a class to parse your string into the Enum for you. This would at least give you some flexibility in handling non-standard entries and much more flexibility for trapping or handling errors than doing any of the workarounds using Enum.Parse/Reflection/etc. A dictionary would work, but could break down if you ever have case issues, etc.

I'd recommend writing a class so you can do:

// I renamed this to GroupType, since it sounds like each element has a single type...
GroupType theType = GroupTypeParser.GetGroupType(theDBString);

This preserves most of your readability without having to change the DB.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
2

If I understand correctly, you need a conversion from string to enum:

enum GroupTypes {
    Unknown = 0,
    OEM = 1,
    CMB = 2
}
static GroupTypes StrToEnum(string str){
    GroupTypes g = GroupTypes.Unknown;
    try {
        object o = Enum.Parse(typeof(GroupTypes), str, true);
        g = (GroupTypes)(o ?? 0);
    } catch {
    }
    return g;
}
// then use it like this
GroupTypes g1 = StrToEnum("OEM");
GroupTypes g2 = StrToEnum("bad value");

You can make it more fancy with generics for the enum type if you wish.

Nerdroid
  • 13,398
  • 5
  • 58
  • 69
dmihailescu
  • 1,625
  • 17
  • 15
2

In VS 2015, you can use nameof

public class LogCategory
{
    public static string Trace;
    public static string Debug;
    public static string Info;
    public static string Warning;
    public static string Error;
}

Usage:

Logger.Write("This is almost like an enum.", nameof(LogCategory.Info));
2

I wanted to avoid using string literals completely, and also I didn't need to have space in item descriptions. More importantly, I wanted to have a mechanism to check if the provided string is a valid item, so I came up with this solution:

public class Seasons
{
    public static string Spring { get; }
    public static string Summer { get; }
    public static string Fall { get; }
    public static string Winter { get; }

    public static bool IsValid(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName))
        {
            return false;
        }

        try
        {           
            return typeof(Seasons).GetProperty(propertyName) != null;
        }
        catch
        {
            return false;
        }       
    }
}

And here is how it works:

void Main()
{
    string s = nameof(Seasons.Fall);
    Console.WriteLine($"Fall is valid: {Seasons.IsValid(s)}"); // true

    s = "WrongSeason";
    Console.WriteLine($"WrongSeason is valid: {Seasons.IsValid(s)}"); // false
}

I tried to refactor IsValid() into a base class and use reflection to read the type (MethodBase.GetCurrentMethod().DeclaringType), but since I wanted to have it static, it returns the base class type, not the inherited type. Your remedy to this will be very welcomed! Here is what I was trying to achieve:

public  class Seasons : ConstantStringsBase
{
    // ... same
}

public  class ConstantStringsBase
{
    public static bool IsValid(string propertyName)
    {       
        return MethodBase.GetCurrentMethod().DeclaringType.GetProperty(propertyName) != null;
    }
}
Siavash Mortazavi
  • 1,382
  • 1
  • 14
  • 18
2

I would make it into a class an avoid an enum altogether. And then with the usage of a typehandler you could create the object when you grab it from the db.

IE:

public class Group
{
    public string Value{ get; set; }
    public Group( string value ){ Value = value; } 
    public static Group TheGroup() { return new Group("OEM"); }
    public static Group OtherGroup() { return new Group("CMB"); }

}
Bryan Rowe
  • 9,185
  • 4
  • 26
  • 34
2

Several good answers above if you like to code the extension yourself

I use CodeHelper.Core.Extensions

enum GroupTypes
{

    [StringValue("OEM")] TheGroup,
    [StringValue("CMB")] TheOtherGroup = "CMB"
}

GroupTypes.TheOtherGroup.ToStringValue()

If you don't have a StringValue Attribute added to the values, the extension returns the normal name (.ToTring()) And super easy to add spaces and other normally not-allowed characters like spaces or starting with numbers

1

Based in other opinions, this is what I come up with. This approach avoids having to type .Value where you want to get the constant value.

I have a base class for all string enums like this:

using System;
using Newtonsoft.Json;

[JsonConverter(typeof(ConstantConverter))]
public class StringEnum: IConvertible
{
    public string Value { get; set; }

    protected StringEnum(string value)
    {
        Value = value;
    }

    public static implicit operator string(StringEnum c)
    {
        return c.Value;
    }
    public string ToString(IFormatProvider provider)
    {
        return Value;
    }

    public TypeCode GetTypeCode()
    {
        throw new NotImplementedException();
    }

    public bool ToBoolean(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }
    //The same for all the rest of IConvertible methods
}

The JsonConverter is like this:

using System;
using Newtonsoft.Json;

class ConstantConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
        {
            serializer.Serialize(writer, null);
        }
        else
        {
            serializer.Serialize(writer, value.ToString());
        }
    }
}

And an actual string enum will be something like this:

public sealed class Colors : StringEnum
{
    public static Colors Red { get { return new Catalog("Red"); } }
    public static Colors Yellow { get { return new Catalog("Yellow"); } }
    public static Colors White { get { return new Catalog("White"); } }

    private Colors(string value) : base(value) { }
}

And with this, you can just use Color.Red to even serialize to json without using the Value property

ayepiz
  • 45
  • 6
1

I even implemented a few enums as suggested by @Even (via class X and public static X members), just to find out later that these days, starting .Net 4.5, there's the right ToString() method.

Now I'm reimplementing everything back to enums.

bohdan_trotsenko
  • 5,167
  • 3
  • 43
  • 70
1

You can use two enums. One for the database and the other for readability.

You just need to make sure they stay in sync, which seems like a small cost. You don't have to set the values, just set the positions the same, but setting the values makes it very clear the two enums are related and prevents errors from rearranging the enum members. And a comment lets the maintenance crew know these are related and must be kept in sync.

// keep in sync with GroupTypes
public enum GroupTypeCodes
{
    OEM,
    CMB
}

// keep in sync with GroupTypesCodes
public enum GroupTypes
{
    TheGroup = GroupTypeCodes.OEM,
    TheOtherGroup = GroupTypeCodes.CMB
}

To use it you just convert to the code first:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = ((GroupTypeCodes)myGroupType).ToString();

Then if you want to make it even more convenient you can add an extension function that only works for this type of enum:

public static string ToString(this GroupTypes source)
{
    return ((GroupTypeCodes)source).ToString();
}

and you can then just do:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = myGroupType.ToString();
toddmo
  • 20,682
  • 14
  • 97
  • 107
1

I was basically looking for the Reflection answer by @ArthurC

Just to extend his answer a little bit, you can make it even better by having a generic function:

    // If you want for a specific Enum
    private static string EnumStringValue(GroupTypes e)
    {
        return EnumStringValue<GroupTypes>(e);
    }

    // Generic
    private static string EnumStringValue<T>(T enumInstance)
    {
        return Enum.GetName(typeof(T), enumInstance);
    } 

Then you can just wrap whatever you have

EnumStringValue(GroupTypes.TheGroup) // if you incorporate the top part

or

EnumStringValue<GroupTypes>(GroupTypes.TheGroup) // if you just use the generic
ThinkBonobo
  • 15,487
  • 9
  • 65
  • 80
1

Adding this class

public class DatabasePreference {
    public DatabasePreference([CallerMemberName] string preferenceName = "") {
        PreferenceName = preferenceName;
    }
    public string PreferenceName;
}

This work is using CallerMemberName to minimize the coding

Using:

//Declare names
public static DatabasePreference ScannerDefaultFlashLight = new DatabasePreference();
public static DatabasePreference ScannerQrCodes = new DatabasePreference();
public static DatabasePreference Scanner1dCodes = new DatabasePreference();

Test it:

Console.WriteLine(ScannerDefaultFlashLight.PreferenceName);
Console.WriteLine(ScannerDefaultFlashLight.Scanner1dCodes);

output:

ScannerDefaultFlashLight
Scanner1dCodes
nyconing
  • 1,092
  • 1
  • 10
  • 33
1

I think the best way use Dictionary <Enum, string> with extension method. It's simple and clear.

Enum

public enum GroupTypes
{
    TheGroup,
    TheOtherGroup
}

Extension

public static class GroupTypesExtensions
{
    private static Dictionary<GroupTypes, string> Descriptions =
         new Dictionary<GroupTypes, string>
         {
                { GroupTypes.TheGroup,"Description Group 1" },
                { GroupTypes.TheOtherGroup, "Description Group 2" }

         };

    public static string Description(this GroupTypes value)
    {
        return Descriptions[value];
    }
}

Use

GroupTypes.TheOtherGroup.Description();
GroupTypes.TheGroup.Description();
Werymag
  • 11
  • 1
0

I didn't need anything robust like storing the string in attributes. I just needed to turn something like MyEnum.BillEveryWeek into "bill every week" or MyEnum.UseLegacySystem into "use legacy system"--basically split the enum by its camel-casing into individiual lower-case words.

public static string UnCamelCase(this Enum input, string delimiter = " ", bool preserveCasing = false)
{
    var characters = input.ToString().Select((x, i) =>
    {

       if (i > 0 && char.IsUpper(x))
       {
           return delimiter + x.ToString(CultureInfo.InvariantCulture);
       }
       return x.ToString(CultureInfo.InvariantCulture);

    });

    var result = preserveCasing
       ? string.Concat(characters)
       : string.Concat(characters).ToLower();

    var lastComma = result.LastIndexOf(", ", StringComparison.Ordinal);

    if (lastComma > -1)
    {
       result = result.Remove(lastComma, 2).Insert(lastComma, " and ");
    }

    return result;
}

MyEnum.UseLegacySystem.UnCamelCase() outputs "use legacy system"

If you have multiple flags set, it will turn that into plain english (comma-delimited except an "and" in place of the last comma).

var myCustomerBehaviour = MyEnum.BillEveryWeek | MyEnum.UseLegacySystem | MyEnum.ChargeTaxes;

Console.WriteLine(myCustomerBehaviour.UnCamelCase());
//outputs "bill every week, use legacy system and charge taxes"
Chad Hedgcock
  • 11,125
  • 3
  • 36
  • 44
0

I've done something like this;

public enum BusinessUnits
{
    NEW_EQUIPMENT = 0,
    USED_EQUIPMENT = 1,
    RENTAL_EQUIPMENT = 2,
    PARTS = 3,
    SERVICE = 4,
    OPERATOR_TRAINING = 5
}

public class BusinessUnitService
{
    public static string StringBusinessUnits(BusinessUnits BU)
    {
        switch (BU)
        {
            case BusinessUnits.NEW_EQUIPMENT: return "NEW EQUIPMENT";
            case BusinessUnits.USED_EQUIPMENT: return "USED EQUIPMENT";
            case BusinessUnits.RENTAL_EQUIPMENT: return "RENTAL EQUIPMENT";
            case BusinessUnits.PARTS: return "PARTS";
            case BusinessUnits.SERVICE: return "SERVICE";
            case BusinessUnits.OPERATOR_TRAINING: return "OPERATOR TRAINING";
            default: return String.Empty;
        }
    }
}

Call it with this;

BusinessUnitService.StringBusinessUnits(BusinessUnits.PARTS)
Mahib
  • 3,977
  • 5
  • 53
  • 62
0

If not all the values of your enum actually have a string and if you want to set those values later, I use classes like this below:

 public class SessionResoult
{
    public enum SessionResoultType
    {
        Success,
        Error,
    }
    public SessionResoult(SessionResoultType sesionResoultType, string value = null)
    {
        Type = sesionResoultType;
        Value = value;
       
    }

    public SessionResoultType Type { get; set; }

    public string Value { get; private set; }
  

    public sealed class Success : SessionResoult
    {
        public Success() : base(SessionResoultType.Success) { }
    }

    public sealed class Error : SessionResoult
    {
        public Error(string value) : base(SessionResoultType.Error, value) { }
    }

    public override string ToString()
    {
        if(this is Success)
        {
            return (SessionResoultType.Success.ToString());
        }else if(this is Error)
        {
            return $"{SessionResoultType.Error}:{this.Value}";
        }
        else { return base.ToString(); }
    }

}

Usage example:

private SessionResoult ok = new SessionResoult.Success(); private SessionResoult error = new SessionResoult.Error("No network available");

Adamo Branz
  • 164
  • 2
  • 7
0

I am sharing it for a different answer.I want to send a string in return for a string. For example Airport = A, Railway = R.

public enum LocationType
    {
        AIRPORT = 1,
        RAILWAY = 2,
        DOWNTOWN = 3
    }
    
public enum ShortLocationType
    {
        A = 1,
        R = 2,
        D = 3
    }

((ShortLocationType)(int)Enum.Parse(typeof(LocationType), "AIRPORT")).ToString();

Output "A"

((ShortLocationType)(int)Enum.Parse(typeof(LocationType), "DOWNTOWN")).ToString();

Output "D"

0

Based on https://stackoverflow.com/a/1343517/1818723 I came up with an enum with TryParse method

public class FancyStringEnum
{
    private FancyStringEnum(string value) { Value = value; }

    public string Value { get; private set; }

    private static List<FancyStringEnum> choices = new List<FancyStringEnum>
    {
        new FancyStringEnum("Small") ,
        new FancyStringEnum("Big Thing") ,
        new FancyStringEnum("Value with Spaces")
    };

    public static FancyStringEnum Small { get { return choices[0]; } }
    public static FancyStringEnum BigThing { get { return choices[1]; } }
    public static FancyStringEnum ValueWithSpaces { get { return choices[2]; } }

    public override string ToString()
    {
        return Value;
    }

    public static bool TryParse(string value, bool ignoreCase, out FancyStringEnum result)
    {
        var sc = StringComparison.InvariantCulture;
        if (ignoreCase)
            sc = StringComparison.InvariantCultureIgnoreCase;

        foreach (var choice in choices)
        {

            if (choice.Value.Equals(value, sc))
            {
                result = choice;
                return true;
            }
        }

        result = new FancyStringEnum(null);
        return false;
    }

    public static FancyStringEnum Parse(string value, bool ignoreCase)
    {
        var sc = StringComparison.InvariantCulture;
        if (ignoreCase)
            sc = StringComparison.InvariantCultureIgnoreCase;

        foreach (var choice in choices)
        {

            if (choice.Value.Equals(value, sc))
            {
                return choice;
            }
        }

        return new FancyStringEnum(null);
    }
}

Pass in type-safe string values as a parameter:

public static void Do(string message, FancyStringEnum value)
{
    if (value == FancyStringEnum.Small)
    {
        //do something
    } else if (value == FancyStringEnum.BigThing)
    {
        //do something else
    }
}

TryParse and Parse in action:

string something = "something"; //substiture with "small" to see it parsed 
if (FancyStringEnum.TryParse(something, true, out var se))
    Console.WriteLine(se.Value);
else
    Console.WriteLine($"unable to parse {something}");

//or    

var v2 = FancyStringEnum.Parse(something, true);
if (v2.Value == null)
    Console.WriteLine($"unable to parse {something}");
else
    Console.WriteLine(v2.Value); //do something with parsed enum

    

Could probably extract a base class to create StringEnums with less code

Pawel Cioch
  • 2,895
  • 1
  • 30
  • 29