Can you add to an enum type in run-time
Asked Answered
L

7

50

If I have an enum type:

public enum Sport
{
    Tennis = 0;
    Football = 1;
    Squash = 2;
    Volleyball = 3;
}

Can I somehow add during run-time:

PingPong = 4
Lydon answered 6/5, 2010 at 8:53 Comment(4)
you could maybe mimic this behaviour using a list or dictionary?Bunsen
https://mcmap.net/q/27028/-extending-an-enum-via-inheritanceZephan
What scenario are you looking to use this in?Syngamy
I have inherited code where there is an enum type that needs to expand based on user input, thereby making enum an inappropriate type, but I need a workaround in the short term.Lydon
G
51

The enum has a backing store, defaulting to int if you don't specify it. It is possible to directly assign values outside of the defined values:

Sport pingPong = (Sport)4;

Then you can check for it:

if (value == (Sport)4) {}

That is why you have the static function Enum.IsDefined() for checking if the actual value falls inside the expected values. Note that the function doesn't work for compound flag values.

bool isValueDefined = Enum.IsDefined(typeof(Sport), value);

EDIT: After Hans Passant's comment: You don't have to use the literal value 4. You could use anything which returns an int. For example:

Dictionary<int, string> AdditionalSports = new Dictionary<int, string>();
AdditionalSports.Add(4, "PingPong");

// Usages: if
if (AdditionalSports.ContainsKey(value))
{
    // Maybe do something with AdditionalSports[value], i.e. "PingPong"
}

// In a switch:
switch (value)
{
case default:
    // Since it won't be found in the enum-defined values
    if (AdditionalSports.ContainsKey(value))
    {
        // Maybe do something with AdditionalSports[value], i.e. "PingPong"
    }
}
Grasso answered 6/5, 2010 at 9:23 Comment(5)
I don't see how that it is at run-time because "PingPong" is being declared in the source.Lydon
It is not, the literal 4 can also be a variable. This is the best you can get.Intricate
What would be the output of Console.WriteLine("My sport is: " + pingpong); ?Lydon
The runtime implicitely calls ToString() on pingpong. If pingpong had a defined enum value (ex. Sport.Tennis), the result is the enum value as text (i.e. "My sport is: Tennis"). If pingpong is not defined (ex. 4), it is the int value itself (i.e. "My sport is: 4"). You could use the AdditionalSports dictionary, i.e. Console.WriteLine("My sport is: " + AdditionalSports[pingpong]); to get "My sport is: PingPong".Grasso
Now you can use C# code generation featureOverlap
U
14

Here is more Object orientated way to maybe achieve what you are trying to achieve. This solution is inspired by early Java approach to enumeration:

struct Sport {
    readonly int value;
    public Sport(int value) {
        this.value = value;
    }
    public static implicit operator int(Sport sport) {
        return sport.value;
    }
    public static implicit operator Sport(int sport) {
        return new Sport(sport);
    }

    public const int Tennis =       0;
    public const int Football =     1;
    public const int Squash =       2;
    public const int Volleyball =   3;
}

//Usage:
Sport sport = Sport.Volleyball;
switch(sport) {
    case Sport.Squash:
        Console.WriteLine("I bounce really high");
        break;
}
Sport rugby = 5;
if (sport == rugby)
    Console.WriteLine("I am really big and eat a lot");

To walk through different featues of this solution.

  1. It's an immutable struct that wraps up an integer value. The value is enforced immutable by readonly keyword.

  2. The only way to create one of these structs is to call the constructor that takes the value as a parameter.

  3. implicit operator int is there so that the structure can be used in the switch bock - i.e. to make the structure convertible to int.

  4. implicit operator Sport is there so that you can assign integer values to the structure i.e. Sport rugby = 5.

  5. const values are the sports known at compile time. They can also be used as case labels.

What I would actually do

public static class Sports {
    public static readonly Sport Football = new Sport("Football");
    public static readonly Sport Tennis = new Sport("Tennis");
}

public class Sport {
    public Sport(string name) {
        Name = name;
    }
    public string Name { get; private set; }

    // override object.Equals
    public override bool Equals(object obj) {
        var other = obj as Sport;
        if(other == null) {
            return false;
        }

        return other == this;
    }

    // override object.GetHashCode
    public override int GetHashCode() {
        return Name.GetHashCode();
    }

    public static bool operator == (Sport sport1, Sport sport2) {
        if(Object.ReferenceEquals(sport1, null) && Object.ReferenceEquals(sport2 , null))
            return true;

        if(Object.ReferenceEquals(sport1, null) || Object.ReferenceEquals(sport2, null))
            return false;

        return sport1.Name == sport2.Name;
    }
    public static bool operator !=(Sport sport1, Sport sport2) {
        return !(sport1 == sport2);
    }
}

This would create a value class Sport that has a name. Depending on your application you can extend this class to provide other attributes and methods. Having this as class gives you more flexibility as you can subclass it.

Sports class provides a static collection of sports that are known at compile time. This is similar to how some .NET frameworks handle named colors (i.e. WPF). Here is the usage:

List<Sport> sports = new List<Sport>();

sports.Add(Sports.Football);
sports.Add(Sports.Tennis);
//What if the name contains spaces?
sports.Add(new Sport("Water Polo"));

var otherSport = new Sport("Other sport");

if(sports.Contains(otherSport)) {
    //Do something
}

foreach(var sport in sports) {
    if(sport == otherSport) {
        //Do Something
    } else if(sport == Sports.Football) {
        //do something else
    }
}

Once you do this, you'd find there is actuall very little need for having an enumeration as any conditional operations on the sport type can be handled within the Sport class.

EDIT Realised that my equality operator will throw a StackOverflowException I always keep forgetting to write Object.ReferenceEquals(obj,null) instead of obj==null, which will recurse infinitely.

Unreserve answered 11/5, 2010 at 1:45 Comment(4)
What do you think of Daniel Rose's answer?Lydon
I would chose neither. Supplementing an enumeration with stuff at runtime is a bit smelly. I would chose a consistent approach for the sports known at compile time and at run time. Example forthcoming in a little bit.Unreserve
I would use public const Sport Tennis = new Sport(0) insteadLogistics
@Logistics That was my first thought too. However, you can't define instances of user defined structs or classes as consts. They have to be either simple builtin types or strings. You an define those as readonly but then you would lose the ability to use them as case labels.Unreserve
N
6

No, you cannot modify types at runtime. You could emit new types, but modifying existing ones is not possible.

Newmark answered 6/5, 2010 at 8:55 Comment(3)
Can an emitted new type replace an existing one?Lydon
No, it can't as this would mean that you would be able to modify existing types at runtime which you can't.Newmark
Why you say you cant add values to an existing enum if the other answears are adding?Unsustainable
F
4

Well yes, you can create and or alter an Enum at runtime by using the EnumBuilder class, See the sample in MSDN

ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
EnumBuilder eb = mb.DefineEnum("Elevation", TypeAttributes.Public, typeof(int));
eb.DefineLiteral("Low", 0);
eb.DefineLiteral("High", 1);
Type finished = eb.CreateType();

Having an enum value is, in my opinion, preferable over dictionaries or list based solution as one uses less memory and no thread side effects.

Here are some samples on how to load the generated enum when you restart your application. I suggest you pick the one that works for you, you could use the methods provided by the System.AddIn namespace as I guess.. or use your IoC.

I use this when I need to generate data for Machine learning as having an Enum value and is preferable over lookup tables (in memory or database) as I just do not have the IO available with these large data sets.

Fugacious answered 12/5, 2018 at 9:47 Comment(2)
This does not appear to be appending to a pre-existing enum list, but rather, creating one from scratch ?Tonl
@WDUK, Well you have no real way to alter an existing assembly and append a data type in it especially if the assembly is strong-named. In any case, the assembly hash will become different, and loading an assembly with the wring hash is a really bad idea.Fugacious
C
2

If the enum can be defined at program startup, you place the enum in a separate assembly and use a bootstrapper that will recompile the enum, overwriting the old version, and then launch the actual application. It's not the cleanest method, but it works.

Crankcase answered 11/5, 2010 at 18:2 Comment(0)
C
0

I was trying the codes above but non worked and gave me bunch of errors this is how I solved it:

using System;
for (int i = 0; i < Enum.GetValues(typeof(ButtonName)).Length; i++){
    Enum.GetValues(typeof(ButtonName)).SetValue((ButtonName)Enum.GetValues(typeof(ButtonName)).GetValue(i), i);
}
public enum ButtonName{
    Person,
    money,
    salary
}

this is how to value them during the runtime

this is where I found idea of how to go through all of them even if they aren't valued.

the most voted answer gave me some inspiration how to set only one even if I got an error from him.

hope this post helps someone

Coiffure answered 31/12, 2021 at 19:20 Comment(0)
E
-3

I would create a complete Enum, with all the values you will ever need like this

Public enum Sport
{
    Tennis = 0;
    Football = 1;
    Squash = 2;
    Volleyball = 3;
    PingPong = 4;
    Rugby = 5;  // for example
}

And then keep a LIST of Invalid Sport entries, so the list would initially contain PingPong and Rugby. Every time you access the Enum, also check with your Invalid Sports list.

Then you can simply adjust your Invalid Sports List to suit, at any stage

Eweneck answered 4/11, 2017 at 16:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.