4

We are currently implementing some kind of "extensible enum class" based on strings. Only a part of this C# code is displayed below, to make the problem easier to understand.

If I run the code below, it writes "BaseValue1" and "BaseValue2" to the console.

If I uncomment the RunClassConstructor line and run the code, it additionally writes "DerivedValue1" and "DerivedValue2" to the console.
This is what I want to achieve, but I want to achieve it without the RunClassConstructor line.

I thought that DerivedEnum.AllKeys would trigger the creation of "DerivedValue1" and "DerivedValue2", but obviously this is not the case.

Is there a possibility to achieve what I want, without forcing the user of these "enum classes" to write some magic code, or to do some kind of dummy initialization?

using System;
using System.Collections.Generic;

namespace ConsoleApplication
{
    public class Program
    {
        static void Main()
        {
            //System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(DerivedEnum).TypeHandle);

            foreach (var value in DerivedEnum.AllKeys)
            {
                Console.WriteLine(value);
            }
        }
    }

    public class BaseEnum
    {
        private static readonly IDictionary<string, BaseEnum> _dictionary = new Dictionary<string, BaseEnum>();

        public static ICollection<string> AllKeys
        {
            get
            {
                return _dictionary.Keys;
            }
        }

        public static readonly BaseEnum BaseValue1 = new BaseEnum("BaseValue1");
        public static readonly BaseEnum BaseValue2 = new BaseEnum("BaseValue2");

        protected BaseEnum(string value)
        {
            _dictionary[value] = this;
        }
    }

    public class DerivedEnum : BaseEnum
    {
        public static readonly DerivedEnum DerivedValue1 = new DerivedEnum("DerivedValue1");
        public static readonly DerivedEnum DerivedValue2 = new DerivedEnum("DerivedValue2");

        protected DerivedEnum(string value)
            : base(value)
        {
        }
    }
}
1

2 Answers 2

2

A static constructor is only invoked when the class is first accessed.

While you did used DerivedEnum.AllKeys, but it is simply inherited from BaseEnum. Therefore DerivedEnum were never directly referenced.

A little hack you could do is actually create a new static property on DerivedEnum that returns the same property from the base class, so when it is called the static constructor of the derived class will be invoked.

public class DerivedEnum : BaseEnum
{
   public new static ICollection<string> AllKeys
   {
       get
       {
           return BaseEnum.AllKeys;
       }
   }
}

UPDATE

You can also use System.Reflexion to dynamically invoke them :

public class BaseEnum
    static BaseEnum()
    {
        // from the types defined in current assembly
        Assembly.GetExecutingAssembly().DefinedTypes
            // for those who are BaseEnum or its derived
            .Where(x => typeof(BaseEnum).IsAssignableFrom(x))
            // invoke their static ctor
            .ToList().ForEach(x => System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(x.TypeHandle));
    }
}

You can also use this code to initialize derived class defined in other assemblies :

AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(x => x.DefinedTypes)
    .Where(x => typeof(BaseEnum).IsAssignableFrom(x))
    .ToList().ForEach(x => System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(x.TypeHandle));
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you. Is there also a solution where only a modification in the BaseEnum is necessary, so that the implementer of DerivedEnum doesn't have to take care implementing the new static property? Probably not, because the BaseEnum doesn't know all its derived classes...
You can also use System.Reflexion to dynamically invoke them See my update.
OK, this would be a nice solution if all derived classes would be in the same Dll as the BaseEnum class. In my code this is unfortunately not the case, because the users of our library might add own derived classes in additional Dlls.
1

In C#, static members are initialized just before the first usage of the class. In your example, you are actually using a member of the base class BaseEnum and bypassing DerivedEnum which is causing only the static members of BaseEnum to be initialized.

You will need to implement the AllKeys property in your derived class. This will insure that the compiler uses the property in your derived class & initializes all of it's members.

And then add a new AllKeys property in your DerivedEnum to override the AllKeys of the BaseEnum.

new public static ICollection<string> AllKeys
{
    get
    {    
        return BaseEnum.AllKeys;
    }
}

2 Comments

Thanks for your reply. I guess you mean AllKeys property, not AllKeys field, right?
@MaDev Correct, thank you for pointing that out. I will update the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.