29

in java im used to extending enum values or overriding methods like this :

    enum SomeEnum
    {

        option1("sv")
        {
            public String toString()
            {
                return "Some value";
            }     
        }, 
        option2;

        private String PassedValue;

        public SomeEnum(String somevalue)
        {
            this.PassedValue = somevalue;
        }

        public SomeEnum()
        {
                this.PassedValue = "Default Value";
        }

        public String getPassedValue()
        {
            return this.PassedValue;
        }

    }

is there a way to do something similar in c# or are enums more limited in c#

Chris McGrath
  • 1,727
  • 3
  • 19
  • 45
  • A very similar already exists with some great answers. You'll find all you need [here](http://stackoverflow.com/questions/469287/c-vs-java-enum-for-those-new-to-c). – Hari Menon Mar 24 '11 at 19:13

5 Answers5

41

I wish enums were more powerful in .Net. And I love .Net! You can use attributes to accomplish the same thing. Write the code below once and use it everywhere. This will be a long answer but I think it's a pretty good solution so have patience!

Usage

SomeEnum e = SomeEnum.ValueTwo;
string description = e.GetDescription();

The Enum

Use attributes to describe the enum and it's values.

[DescriptiveEnumEnforcement(DescriptiveEnumEnforcement.EnforcementTypeEnum.ThrowException)]
public enum SomeEnum
{
    [Description("Value One")]
    ValueOne,

    [Description("Value Two")]
    ValueTwo,

    [Description("Value 3")]
    ValueThree
}

DescriptionAttribute

/// <summary>Indicates that an enum value has a description.</summary>
[AttributeUsage(AttributeTargets.Field)]
public class DescriptionAttribute : System.Attribute
{
    /// <summary>The description for the enum value.</summary>
    public string Description { get; set; }

    /// <summary>Constructs a new DescriptionAttribute.</summary>
    public DescriptionAttribute() { }

    /// <summary>Constructs a new DescriptionAttribute.</summary>
    /// <param name="description">The initial value of the Description property.</param>
    public DescriptionAttribute(string description)
    {
        this.Description = description;
    }

    /// <summary>Returns the Description property.</summary>
    /// <returns>The Description property.</returns>
    public override string ToString()
    {
        return this.Description;
    }
}

DescriptiveEnumEnforcementAttribute

An attribute to ensure your enum's are properly configured.

/// <summary>Indicates whether or not an enum must have a NameAttribute and a DescriptionAttribute.</summary>
[AttributeUsage(AttributeTargets.Enum)]
public class DescriptiveEnumEnforcementAttribute : System.Attribute
{
    /// <summary>Defines the different types of enforcement for DescriptiveEnums.</summary>
    public enum EnforcementTypeEnum
    {
        /// <summary>Indicates that the enum must have a NameAttribute and a DescriptionAttribute.</summary>
        ThrowException,

        /// <summary>Indicates that the enum does not have a NameAttribute and a DescriptionAttribute, the value will be used instead.</summary>
        DefaultToValue
    }

    /// <summary>The enforcement type for this DescriptiveEnumEnforcementAttribute.</summary>
    public EnforcementTypeEnum EnforcementType { get; set; }

    /// <summary>Constructs a new DescriptiveEnumEnforcementAttribute.</summary>
    public DescriptiveEnumEnforcementAttribute()
    {
        this.EnforcementType = EnforcementTypeEnum.DefaultToValue;
    }

    /// <summary>Constructs a new DescriptiveEnumEnforcementAttribute.</summary>
    /// <param name="enforcementType">The initial value of the EnforcementType property.</param>
    public DescriptiveEnumEnforcementAttribute(EnforcementTypeEnum enforcementType)
    {
        this.EnforcementType = enforcementType;
    }
}

Getting the Description

/// <summary>Provides functionality to enhance enumerations.</summary>
public static partial class EnumUtil
{
    /// <summary>Returns the description of the specified enum.</summary>
    /// <param name="value">The value of the enum for which to return the description.</param>
    /// <returns>A description of the enum, or the enum name if no description exists.</returns>
    public static string GetDescription(this Enum value)
    {
        return GetEnumDescription(value);
    }

    /// <summary>Returns the description of the specified enum.</summary>
    /// <param name="value">The value of the enum for which to return the description.</param>
    /// <returns>A description of the enum, or the enum name if no description exists.</returns>
    public static string GetDescription<T>(object value)
    {
        return GetEnumDescription(value);
    }

    /// <summary>Returns the description of the specified enum.</summary>
    /// <param name="value">The value of the enum for which to return the description.</param>
    /// <returns>A description of the enum, or the enum name if no description exists.</returns>
    public static string GetEnumDescription(object value)
    {
        if (value == null)
        return null;

        Type type = value.GetType();

        //Make sure the object is an enum.
        if (!type.IsEnum)
            throw new ApplicationException("Value parameter must be an enum.");

        FieldInfo fieldInfo = type.GetField(value.ToString());
        object[] descriptionAttributes = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

        //If no DescriptionAttribute exists for this enum value, check the DescriptiveEnumEnforcementAttribute and decide how to proceed.
        if (descriptionAttributes == null || descriptionAttributes.Length == 0)
        {
            object[] enforcementAttributes = fieldInfo.GetCustomAttributes(typeof(DescriptiveEnumEnforcementAttribute), false);

            //If a DescriptiveEnumEnforcementAttribute exists, either throw an exception or return the name of the enum instead.
            if (enforcementAttributes != null && enforcementAttributes.Length == 1)
            {
                DescriptiveEnumEnforcementAttribute enforcementAttribute = (DescriptiveEnumEnforcementAttribute)enforcementAttributes[0];

                if (enforcementAttribute.EnforcementType == DescriptiveEnumEnforcementAttribute.EnforcementTypeEnum.ThrowException)
                    throw new ApplicationException("No Description attributes exist in enforced enum of type '" + type.Name + "', value '" + value.ToString() + "'.");

                return GetEnumName(value);
            }
            else //Just return the name of the enum.
                return GetEnumName(value);
        }
        else if (descriptionAttributes.Length > 1)
            throw new ApplicationException("Too many Description attributes exist in enum of type '" + type.Name + "', value '" + value.ToString() + "'.");

        //Return the value of the DescriptionAttribute.
        return descriptionAttributes[0].ToString();
    }
}
Josh M.
  • 26,437
  • 24
  • 119
  • 200
  • so i can use attribute classes to essentially give the eum a set of values? would i need to still use extension methods to retrieve the values or or will the attribute add this capability i essentially want use SomeEnum.Option1.getPassedValue(); to retrieve format strings for a AtomicParsley app im writing – Chris McGrath Mar 24 '11 at 19:15
  • 1
    I love parsley! Delicious. This code I posted will do just what you've asked for, check out the "Usage," above. You can retrieve the values however you want, I made it an extension method because it will just make it cleaner to call the method. – Josh M. Mar 24 '11 at 19:46
  • I was struggling to have a generic enumeration over all values of `T` and getting the descriptions, turns out my cast to the enum was wrong. If `e` is your enum value as `int` and `enumType` is the `Type` of enum, you can get the description with `(Enum.ToObject(enumType, e) as Enum).GetDescription()` – Superman.Lopez Jun 13 '20 at 02:26
  • How would you go about casting from the description back to the underlying enum value. – Reahreic Sep 24 '20 at 12:10
  • You shouldn't use the description as the "key" you're passing around, you should use the value. The description should only be used for display purposes. So to answer your question, you should never translate from an enum value's description. – Josh M. Sep 29 '20 at 13:17
26

Enums in C# are for (integer) values only; they cannot have special methods or constructors like in Java.

However, you can define extension methods that work on enums to achieve nearly the same effect:

public enum MyEnum {
    Foo = 1,
    Bar = 2,
    Default = Foo
}

public static class MyEnumExtensions
{
    public static Widget ToWidget(this MyEnum enumValue) {
        switch (enumValue) {
        case MyEnum.Foo:
            return new Widget("Foo!");

        case MyEnum.Bar:
            return new Widget("Bar...");

        default:
            return null;
        }
    }
}

Then you could say:

var val = MyEnum.Foo;
var widget = val.ToWidget();
Cameron
  • 96,106
  • 25
  • 196
  • 225
  • There's a much cleaner way to accomplish this using attributes, which doesn't require hard-coding literals. – KeithS Mar 24 '11 at 19:40
  • 3
    @Keith: Well yes, this is a rather convoluted example. I'm just trying to show how extension methods might be used with enums :-) – Cameron Mar 24 '11 at 19:42
  • 1
    @KeithS but you are hard coding literals into the attribute – cubesnyc May 17 '18 at 21:18
  • The attribute value, like for a "Description" attribute or workalike, can be a reference to mutable data; app settings, resource file, INI, database query. Change that data, you change the labels, no recompile needed, *unless* you are adding or removing them (in which case you probably need a recompile anyway to use the new value). Also, attributes consolidate the hardcoding at the definition of the enum; easier to find than in your extension method library. – KeithS May 17 '18 at 21:36
  • I personally prefer this solution, because even though it uses hard-coded string literals, it does not use reflection so it should generally run faster and use fewer allocations. – Yoshi Askharoun Jan 07 '22 at 00:20
3

enums in C# are basically just named primitives. They typically are based on int, but can be based on any numeric primitive. So C# does not offer nearly the functionality that Java enums do. The trade off is C# enums are much lighter, whereas Java enums are full fledged objects.

public enum FooBar : int {
    Foo = 1,
    Bar = 2
}

The above enum is not much different from an int, except we can now use FooBar.Foo instead of the literal 1. You can cast the enum back and forth between ints, and Enum has some helper functions that can help when working with enums. But that's about it for C#, they are much like C or C++ enums.

Matt Greer
  • 60,826
  • 17
  • 123
  • 123
2

You can do something similar in C# using Extension methods.

enum Test
{
    Value1,
    Value2,
    Value3
}

static class TextExtensions
{
    public static string Value(this Test value)
    {
        string stringValue = default(String);

        switch (value)
        {
            case Test.Value1:
                {
                    stringValue = "some value 1";
                } break;

            case Test.Value2:
                {
                    stringValue = "some value 2";
                }; break;

            case Test.Value3:
                {
                    stringValue = "some value 3";
                }; break;
        }

        return stringValue;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Console.Write(Test.Value1.Value());
        Console.ReadLine();
    }
}
Brandon Moretz
  • 7,512
  • 3
  • 33
  • 43
0

Below is my implementation on how to wrap Enums to implement custom functionality for your business cases. It requires a bit of boilerplate but gives you ability to create your own validation and functionality with custom class that whilst still getting all the benefits of a Flag Enum.

Code from .Net Fiddle:

using System;

public class MyClass
{
    /* Flag Enum - Access via FilePerms */
    [Flags] enum Flag_FilePerms : int
    {
        None = 0,
        Create = 1,
        Read = 2,
        Update = 4,
        Delete = 8
    }

    /* FlagEnum base class */
    abstract class FlagEnum
    {
        protected string __alias__;
        protected Type __etype__;
        protected Type __utype__;

        public FlagEnum(string sAlias, Type etype)
        {
            if (!etype.IsEnum)
                throw new Exception($"etype is not an Enum Type. Got: {etype.Name}");
            this.__alias__ = sAlias;
            this.__etype__ = etype;
            this.__utype__ = Enum.GetUnderlyingType(etype);
            // Do validation here. 
            // Eg. ensuring that sequence follows power of 2 with None = 0 as first entry ...
            //     Note: .net does not validate this automatically with the Flags() 
        }
        // implement custom methods...
    }


    /* Enum wrapper */
    class Flag_FilePerms_Wrapper : FlagEnum 
    {
        public Flag_FilePerms None     = Flag_FilePerms.None;
        public Flag_FilePerms Create   = Flag_FilePerms.Create;
        public Flag_FilePerms Read     = Flag_FilePerms.Read;
        public Flag_FilePerms Update   = Flag_FilePerms.Update;
        public Flag_FilePerms Delete   = Flag_FilePerms.Delete;
        public Flag_FilePerms_Wrapper(string sAlias) : base(sAlias, typeof(Flag_FilePerms))
        {
        }
    }
    
    private static void pc(object o){Console.Write($"{o}\n");}
    

    /* singleton for use in business logic */
    static Flag_FilePerms_Wrapper FilePerms = new Flag_FilePerms_Wrapper("FilePerms");


    static void Main(string[] args)
    {
        /* business logic */
        var perms = FilePerms.Update | FilePerms.Create;

        pc($"perms.HasFlag(FilePerms.Create): {perms.HasFlag(FilePerms.Create)}"); /* True */
        pc($"perms.HasFlag(FilePerms.Delete): {perms.HasFlag(FilePerms.Delete)}"); /* False */

        var perms_none = FilePerms.None;

        pc($"perms_none == FilePerms.None: {perms_none == FilePerms.None}"); /* True */
        pc($"perms == FilePerms.None: {perms == FilePerms.None}"); /* False */
    }

}

You could implement different other classes of Enums and implement functionality specific for each usecase.

For example:

  • FlagEnum:
    • Add custom validation to ensure None entry and power of two increments
    • Add functionality specific to flag handling
  • IntEnum:
    • Restrict multiple selections
    • Value agnostic (unlike FlagEnum)
    • usecase: error codes etc..
  • StrEnum:
    • Where Enum has only the name and no value specified
    • Methods written to handle string as only valid parse parameter
      • out of the box Enum.parse handles both string and numeric value and a numeric value may be ambiguous to your business case.
    • Usecase: Workflow states
Timothy C. Quinn
  • 3,739
  • 1
  • 35
  • 47