m4rc
m4rc

Reputation: 3006

Parsing string to match enum value

If I have an enum of char's

public enum Action
{
    None,
    Address = 'A',
    Amendment = 'C',
    Normal = 'N'
}

What is the best way to parse a single character string to match the corresponding enum char and match None if not found. TryParse matches the Name and not the value.

For instance if the string I had were "C" I would want to get Action.Amendement

Thanks in advance

Upvotes: 5

Views: 3251

Answers (5)

Robetto
Robetto

Reputation: 789

Based on @Mr Moose I want to provide my 2 cents of code. It contains the same method but also includes the reverse method. So in a serialization deserialization case it can be combined.

Include in your code a EnumExtensions class:

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

public static class EnumExtensions
{
    /// <summary>
    /// Converts the bare enum value to a string using the <see cref="DescriptionAttribute"/>
    /// that was appplied to it.
    /// </summary>
    /// <typeparam name="TEn"></typeparam>
    /// <param name="enumValue"></param>
    /// <returns></returns>
    public static string ToDescription<TEn>(this TEn enumValue) where TEn : struct
    {
        FieldInfo fi = enumValue.GetType().GetField(enumValue.ToString());

        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

        return attributes.Length > 0 ? attributes[0].Description : enumValue.ToString();
    }

    /// <summary>
    /// Does the reverse lookup. If there is an enum member with the string <paramref name="enumString"/>
    /// as <see cref="DescriptionAttribute"/> it will be returned, otherwise the fallback value in
    /// <paramref name="fallback"/> will be returned.
    /// </summary>
    /// <typeparam name="TEn">Type of the enum in question.</typeparam>
    /// <param name="enumString">String serialization of Description annotated enum.</param>
    /// <param name="fallback">Default value to return.</param>
    /// <returns>Either the found value or the fallback.</returns>
    public static TEn FromDescription<TEn>(this string enumString, TEn fallback = default(TEn)) where TEn : struct
    {
        if (enumString != null)
        {
            FieldInfo[] fieldInfo = typeof(TEn).GetFields();

            foreach (var fi in fieldInfo)
            {
                DescriptionAttribute[] attributes =
                    (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
                if (attributes.Any(att => att.Description == enumString))
                {
                    object rawConstantValue = fi.GetRawConstantValue();
                    return (TEn)rawConstantValue;
                }
            }
        }

        return fallback;
    }
}

And supply Enums with the DescriptionAttribute as seen already:

public enum ComparisonOperators
{
    [Description("=")]
    Equal,

    [Description("<>")]
    Unequal,

    [Description("<")]
    LessThan,

    [Description("<=")]
    LessThanOrEqual,

    [Description(">")]
    GreaterThan,

    [Description(">=")]
    GreaterThanOrEqual
}

use it like so:

string v = "<>";
ComparisonOperators x = v.FromDescription(ComparisonOperators.Equal);
string w = x.ToDescription();
Debug.Assert(v==w);  //ok

Upvotes: 0

Mr Moose
Mr Moose

Reputation: 6344

I'd personally leave them as integers and use DescriptionAttributes and a utility class to get the description attribue for the type. Then you could use more than just a character to display what you wanted.

An example of this is;

    /// <summary>
    /// Returns the string value defined by the description attribute of the given enum.
    /// If no description attribute is available, then it returns the string representation of the enum.
    /// </summary>
    /// <param name="value">Enum to use</param>
    /// <returns>String representation of enum using Description attribute where possible</returns>
    public static string StringValueOf(Enum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());
        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (attributes.Length > 0)
        {
            return attributes[0].Description;
        }
        else
        {
            return value.ToString();
        }
    }

where your enum is defined as something like;

public enum Action
{
    None,
    [DescriptionAttribute("A")]
    Address,
    [DescriptionAttribute("C")]
    Amendment,
    [DescriptionAttribute("N")]
    Normal
}

Upvotes: 3

Adrian Thompson Phillips
Adrian Thompson Phillips

Reputation: 7141

Enums are numeric types behind the scenes. You could try a class of public strings:

public class Action()
{
    public const string A = "Address";
    public const string C = "Amendment";
}

If you want to do it 2 ways then you probably want to use a 2 way dictionary collection.

Upvotes: 0

Darin Dimitrov
Darin Dimitrov

Reputation: 1038800

char c = 'C'; // existent value
var action = Enum.GetValues(typeof(Action)).Cast<Action>().FirstOrDefault(a => (char)a == c);
// action = Action.Amendment

and:

char c = 'X'; // non existent value
var action = Enum.GetValues(typeof(Action)).Cast<Action>().FirstOrDefault(a => (char)a == c);
// action = Action.None

Upvotes: 5

Oskar Kjellin
Oskar Kjellin

Reputation: 21880

Just cast it :

Action f = (Action)'C';

If you had a string and were positive it was at least 1 character you can do:

Action f = (Action)"C"[0];

Upvotes: 4

Related Questions