[c#] How to define an enum with string value?

I am trying to define an Enum and add valid common separators which used in CSV or similar files. Then I am going to bind it to a ComboBox as a data source so whenever I add or remove from the Enum definition, I would not need to change anything in the combo box.

The problem is how can I define enum with string representation, something like:

public enum SeparatorChars{Comma = ",", Tab = "\t", Space = " "}

This question is related to c# string enums

The answer is


I created a base class for creating string-valued enums in .NET. It is just one C# file that you can copy & paste into your projects, or install via NuGet package named StringEnum.

Usage:

///<completionlist cref="HexColor"/> 
class HexColor : StringEnum<HexColor>
{
    public static readonly HexColor Blue = New("#FF0000");
    public static readonly HexColor Green = New("#00FF00");
    public static readonly HexColor Red = New("#000FF");
}

Features

  • Your StringEnum looks somewhat similar to a regular enum:
    // Static Parse Method
    HexColor.Parse("#FF0000") // => HexColor.Red
    HexColor.Parse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.Parse("invalid") // => throws InvalidOperationException

    // Static TryParse method.
    HexColor.TryParse("#FF0000") // => HexColor.Red
    HexColor.TryParse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.TryParse("invalid") // => null

    // Parse and TryParse returns the preexistent instances
    object.ReferenceEquals(HexColor.Parse("#FF0000"), HexColor.Red) // => true

    // Conversion from your `StringEnum` to `string`
    string myString1 = HexColor.Red.ToString(); // => "#FF0000"
    string myString2 = HexColor.Red; // => "#FF0000" (implicit cast)
  • Intellisense will suggest the enum name if the class is annotated with the xml comment <completitionlist>. (Works in both C# and VB): i.e.

Intellisense demo

Instalation

Either:

  • Install latest StringEnum NuGet package, which is based on .Net Standard 1.0 so it runs on .Net Core >= 1.0, .Net Framework >= 4.5, Mono >= 4.6, etc.
  • Or paste the following StringEnum base class to your project. (latest version)
    public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new()
    {
        protected string Value;
        private static IList<T> valueList = new List<T>();
        protected static T New(string value)
        {
            if (value == null)
                return null; // the null-valued instance is null.

            var result = new T() { Value = value };
            valueList.Add(result);
            return result;
        }

        public static implicit operator string(StringEnum<T> enumValue) => enumValue.Value;
        public override string ToString() => Value;

        public static bool operator !=(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value != o2?.Value;
        public static bool operator ==(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value == o2?.Value;

        public override bool Equals(object other) => this.Value.Equals((other as T)?.Value ?? (other as string));
        bool IEquatable<T>.Equals(T other) => this.Value.Equals(other.Value);
        public override int GetHashCode() => Value.GetHashCode();

        /// <summary>
        /// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else throws InvalidOperationException.
        /// </summary>
        /// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
        /// <param name="caseSensitive">If true, the strings must match case sensitivity.</param>
        public static T Parse(string value, bool caseSensitive = false)
        {
            var result = TryParse(value, caseSensitive);
            if (result == null)
                throw new InvalidOperationException((value == null ? "null" : $"'{value}'") + $" is not a valid {typeof(T).Name}");

            return result;
        }

        /// <summary>
        /// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else returns null.
        /// </summary>
        /// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
        /// <param name="caseSensitive">If true, the strings must match case sensitivity.</param>
        public static T TryParse(string value, bool caseSensitive = false)
        {
            if (value == null) return null;
            if (valueList.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initialization
            var field = valueList.FirstOrDefault(f => f.Value.Equals(value,
                    caseSensitive ? StringComparison.Ordinal
                                  : StringComparison.OrdinalIgnoreCase));
            // Not using InvariantCulture because it's only supported in NETStandard >= 2.0

            if (field == null)
                return null;

            return field;
        }
    }
  • For Newtonsoft.Json serialization support, copy this extended version instead. StringEnum.cs

I realized after the fact that this code is similar to Ben's answer. I sincerely wrote it from scratch. However I think it has a few extras, like the <completitionlist> hack, the resulting class looks more like an Enum, no use of reflection on Parse(), the NuGet package and repo where I will hopefully address incoming issues and feedback.


For a simple enum of string values (or any other type):

public static class MyEnumClass
{
    public const string 
        MyValue1 = "My value 1",
        MyValue2 = "My value 2";
}

Usage: string MyValue = MyEnumClass.MyValue1;


Maybe it's too late, but here it goes.

We can use the attribute EnumMember to manage Enum values.

public enum EUnitOfMeasure
{
    [EnumMember(Value = "KM")]
    Kilometer,
    [EnumMember(Value = "MI")]
    Miles
}

This way the result value for EUnitOfMeasure will be KM or MI. This also can be seen in Andrew Whitaker answer.


As far as I know, you will not be allowed to assign string values to enum. What you can do is create a class with string constants in it.

public static class SeparatorChars
{
    public static String Comma { get { return ",";} } 
    public static String Tab { get { return "\t,";} } 
    public static String Space { get { return " ";} } 
}

We can't define enumeration as string type. The approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong.

If you need more details on enumeration please follow below link,that link will help you to understand enumeration. Enumeration

@narendras1414


What I have recently begun doing is using Tuples

public static (string Fox, string Rabbit, string Horse) Animals = ("Fox", "Rabbit", "Horse");
...
public static (string Comma, string Tab, string Space) SeparatorChars = (",", "\t", " ");

Building on some of the answers here I have implemented a reusable base class that mimics the behaviour of an enum but with string as the underlying type. It supports various operations including:

  1. getting a list of possible values
  2. converting to string
  3. comparison with other instances via .Equals, ==, and !=
  4. conversion to/from JSON using a JSON.NET JsonConverter

This is the base class in it's entirety:

public abstract class StringEnumBase<T> : IEquatable<T>
    where T : StringEnumBase<T>
{
    public string Value { get; }

    protected StringEnumBase(string value) => this.Value = value;

    public override string ToString() => this.Value;

    public static List<T> AsList()
    {
        return typeof(T)
            .GetProperties(BindingFlags.Public | BindingFlags.Static)
            .Where(p => p.PropertyType == typeof(T))
            .Select(p => (T)p.GetValue(null))
            .ToList();
    }

    public static T Parse(string value)
    {
        List<T> all = AsList();

        if (!all.Any(a => a.Value == value))
            throw new InvalidOperationException($"\"{value}\" is not a valid value for the type {typeof(T).Name}");

        return all.Single(a => a.Value == value);
    }

    public bool Equals(T other)
    {
        if (other == null) return false;
        return this.Value == other?.Value;
    }

    public override bool Equals(object obj)
    {
        if (obj == null) return false;
        if (obj is T other) return this.Equals(other);
        return false;
    }

    public override int GetHashCode() => this.Value.GetHashCode();

    public static bool operator ==(StringEnumBase<T> a, StringEnumBase<T> b) => a?.Equals(b) ?? false;

    public static bool operator !=(StringEnumBase<T> a, StringEnumBase<T> b) => !(a?.Equals(b) ?? false);

    public class JsonConverter<T> : Newtonsoft.Json.JsonConverter
        where T : StringEnumBase<T>
    {
        public override bool CanRead => true;

        public override bool CanWrite => true;

        public override bool CanConvert(Type objectType) => ImplementsGeneric(objectType, typeof(StringEnumBase<>));

        private static bool ImplementsGeneric(Type type, Type generic)
        {
            while (type != null)
            {
                if (type.IsGenericType && type.GetGenericTypeDefinition() == generic)
                    return true;

                type = type.BaseType;
            }

            return false;
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JToken item = JToken.Load(reader);
            string value = item.Value<string>();
            return StringEnumBase<T>.Parse(value);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value is StringEnumBase<T> v)
                JToken.FromObject(v.Value).WriteTo(writer);
        }
    }
}

And this is how you would implement your "string enum":

[JsonConverter(typeof(JsonConverter<Colour>))]
public class Colour : StringEnumBase<Colour>
{
    private Colour(string value) : base(value) { }

    public static Colour Red => new Colour("red");
    public static Colour Green => new Colour("green");
    public static Colour Blue => new Colour("blue");
}

Which could be used like this:

public class Foo
{
    public Colour colour { get; }

    public Foo(Colour colour) => this.colour = colour;

    public bool Bar()
    {
        if (this.colour == Colour.Red || this.colour == Colour.Blue)
            return true;
        else
            return false;
    }
}

I hope someone finds this useful!


It is kind of late for answer, but maybe it helps someone in future. I found it easier to use struct for this kind of problem.

Following sample is copy pasted part from MS code:

namespace System.IdentityModel.Tokens.Jwt
{
    //
    // Summary:
    //     List of registered claims from different sources http://tools.ietf.org/html/rfc7519#section-4
    //     http://openid.net/specs/openid-connect-core-1_0.html#IDToken
    public struct JwtRegisteredClaimNames
    {
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Actort = "actort";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Typ = "typ";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Sub = "sub";
        //
        // Summary:
        //     http://openid.net/specs/openid-connect-frontchannel-1_0.html#OPLogout
        public const string Sid = "sid";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Prn = "prn";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Nbf = "nbf";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Nonce = "nonce";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string NameId = "nameid";

    }
}

You can't do this with enums, but you can do it like that:

public static class SeparatorChars
{
    public static string Comma = ",";

    public static string Tab = "\t";

    public static string Space = " ";
}

You can't, because enum can only be based on a primitive numeric type. You could try using a Dictionary instead:

Dictionary<String, char> separators = new Dictionary<string, char>
{
    {"Comma", ','}, 
    {"Tab",  '\t'}, 
    {"Space", ' '},
};

Alternatively, you could use a Dictionary<Separator, char> or Dictionary<Separator, string> where Separator is a normal enum:

enum Separator
{
    Comma,
    Tab,
    Space
}

which would be a bit more pleasant than handling the strings directly.


You can achieve it but will required bit of work.

  1. Define an attribute class which will contain the string value for enum.
  2. Define an extension method which will return back the value from the attribute. Eg..GetStringValue(this Enum value) will return attribute value.
  3. Then you can define the enum like this..
public enum Test : int {
    [StringValue("a")]
    Foo = 1,
    [StringValue("b")]
    Something = 2        
} 
  1. To get back the value from Attrinbute Test.Foo.GetStringValue();

Refer : Enum With String Values In C#


I wish there were a more elegant solution, like just allowing string type enum in the language level, but it seems that it is not supported yet. The code below is basically the same idea as other answers, but I think it is shorter and it can be reused. All you have to do is adding a [Description("")] above each enum entry and add a class that has 10 lines.

The class:

public static class Extensions
{
    public static string ToStringValue(this Enum en)
    {
        var type = en.GetType();
        var memInfo = type.GetMember(en.ToString());
        var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
        var stringValue = ((DescriptionAttribute)attributes[0]).Description;
        return stringValue;
    }
}

Usage:

    enum Country
    {
        [Description("Deutschland")]
        Germany,
        [Description("Nippon")]
        Japan,
        [Description("Italia")]
        Italy,
    }

    static void Main(string[] args)
    {
        Show(new[] {Country.Germany, Country.Japan, Country.Italy});

        void Show(Country[] countries)
        {
            foreach (var country in countries)
            {
                Debug.WriteLine(country.ToStringValue());
            }
        }
    }

A class that emulates enum behaviour but using string instead of int can be created as follows...

public class GrainType
{
    private string _typeKeyWord;

    private GrainType(string typeKeyWord)
    {
        _typeKeyWord = typeKeyWord;
    }

    public override string ToString()
    {
        return _typeKeyWord;
    }

    public static GrainType Wheat = new GrainType("GT_WHEAT");
    public static GrainType Corn = new GrainType("GT_CORN");
    public static GrainType Rice = new GrainType("GT_RICE");
    public static GrainType Barley = new GrainType("GT_BARLEY");

}

Usage...

GrainType myGrain = GrainType.Wheat;

PrintGrainKeyword(myGrain);

then...

public void PrintGrainKeyword(GrainType grain) 
{
    Console.Writeline("My Grain code is " + grain.ToString());   // Displays "My Grain code is GT_WHEAT"
}

Enumaration Class

 public sealed class GenericDateTimeFormatType
    {

        public static readonly GenericDateTimeFormatType Format1 = new GenericDateTimeFormatType("dd-MM-YYYY");
        public static readonly GenericDateTimeFormatType Format2 = new GenericDateTimeFormatType("dd-MMM-YYYY");

        private GenericDateTimeFormatType(string Format)
        {
            _Value = Format;
        }

        public string _Value { get; private set; }
    }

Enumaration Consuption

public static void Main()
{
       Country A = new Country();

       A.DefaultDateFormat = GenericDateTimeFormatType.Format1;

      Console.ReadLine();
}

It works for me..

   public class ShapeTypes
    {
        private ShapeTypes() { }
        public static string OVAL
        {
            get
            {
                return "ov";
            }
            private set { }
        }

        public static string SQUARE
        {
            get
            {
                return "sq";
            }
            private set { }
        }

        public static string RECTANGLE
        {
            get
            {
                return "rec";
            }
            private set { }
        }
    }

Well first you try to assign strings not chars, even if they are just one character. use ',' instead of ",". Next thing is, enums only take integral types without char you could use the unicode value, but i would strongly advice you not to do so. If you are certain that these values stay the same, in differnt cultures and languages, i would use a static class with const strings.


While it is really not possible to use a char or a string as the base for an enum, i think this is not what you really like to do.

Like you mentioned you'd like to have an enum of possibilities and show a string representation of this within a combo box. If the user selects one of these string representations you'd like to get out the corresponding enum. And this is possible:

First we have to link some string to an enum value. This can be done by using the DescriptionAttribute like it is described here or here.

Now you need to create a list of enum values and corresponding descriptions. This can be done by using the following method:

/// <summary>
/// Creates an List with all keys and values of a given Enum class
/// </summary>
/// <typeparam name="T">Must be derived from class Enum!</typeparam>
/// <returns>A list of KeyValuePair&lt;Enum, string&gt; with all available
/// names and values of the given Enum.</returns>
public static IList<KeyValuePair<T, string>> ToList<T>() where T : struct
{
    var type = typeof(T);

    if (!type.IsEnum)
    {
        throw new ArgumentException("T must be an enum");
    }

    return (IList<KeyValuePair<T, string>>)
            Enum.GetValues(type)
                .OfType<T>()
                .Select(e =>
                {
                    var asEnum = (Enum)Convert.ChangeType(e, typeof(Enum));
                    return new KeyValuePair<T, string>(e, asEnum.Description());
                })
                .ToArray();
}

Now you'll have a list of key value pairs of all enums and their description. So let's simply assign this as a data source for a combo box.

var comboBox = new ComboBox();
comboBox.ValueMember = "Key"
comboBox.DisplayMember = "Value";
comboBox.DataSource = EnumUtilities.ToList<Separator>();

comboBox.SelectedIndexChanged += (sender, e) =>
{
    var selectedEnum = (Separator)comboBox.SelectedValue;
    MessageBox.Show(selectedEnum.ToString());
}

The user sees all the string representations of the enum and within your code you'll get the desired enum value.


For people arriving here looking for an answer to a more generic question, you can extend the static class concept if you want your code to look like an enum.

The following approach works when you haven't finalised the enum names you want and the enum values are the string representation of the enam name; use nameof() to make your refactoring simpler.

public static class Colours
{
    public static string Red => nameof(Red);
    public static string Green => nameof(Green);
    public static string Blue => nameof(Blue);
}

This achieves the intention of an enum that has string values (such as the following pseudocode):

public enum Colours
{
    "Red",
    "Green",
    "Blue"
}

Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to string

How to split a string in two and store it in a field String method cannot be found in a main class method Kotlin - How to correctly concatenate a String Replacing a character from a certain index Remove quotes from String in Python Detect whether a Python string is a number or a letter How does String substring work in Swift How does String.Index work in Swift swift 3.0 Data to String? How to parse JSON string in Typescript

Examples related to enums

Enums in Javascript with ES6 Check if value exists in enum in TypeScript Why Python 3.6.1 throws AttributeError: module 'enum' has no attribute 'IntFlag'? TypeScript enum to object array How can I loop through enum values for display in radio buttons? How to get all values from python enum class? Get enum values as List of String in Java 8 enum to string in modern C++11 / C++14 / C++17 and future C++20 Implementing Singleton with an Enum (in Java) Swift: Convert enum value to String?