0

I have an abstract base class, and subclasses who all have an automatically generated Instance static field of their own type, done through using genericity "in between" the base and sub classes (see code below).

When those instances are created, I register them in a dictionary based on specific "identifying" fields overwritten in subclass constructors, in order to access them somewhere else in the code.

I could register them in their constructors, however I don't want to have to drag and copy/paste this same bit of code in every such subclass in order to register them.

public abstract class BaseClass
{
        // Declaration of fields and methods
}

public class GenericBaseClass<T> : BaseClass where T : GenericBaseClass<T>
{
        private static T instance = ((T) Activator.CreateInstance(typeof(T), true)).Register();
        public static T Instance => instance;

        public T Register()
        {
                // Registers Instance in a dictionary.
        }

}

public class Subclass1 : GenericBaseClass<Subclass1>
{
        private Subclass1()
        {
                // Modification of protected fields.
        }
}

Doing this enables me to call Subclass1.Instance, and by doing so, this instance gets registered in my dictionary, however it doesn't get registered before I try to access it, and only Subclass1 gets that treatment, not other subclasses.

I followed this answer https://stackoverflow.com/a/34726769/13738641 (in the updated section) and made a static constructor for BaseClass and for GenericBaseClass hoping it would initialize static fields for those subclasses, to no avail.

It appears from debugging that the BaseClass static constructor is indeed called, and it does fetch the right subclasses, but it doesn't throw an error, nor does it calls the subclass (static) constructors. Now I could be going crazy, but I could have sworn it worked once before I tried modifying something before reverting back to that, only for it not to work anymore.

I've tried adding a static constructor to the generic class which I hoped would be called once for each subclass, but this constructor only gets called once (when I access some subclass's instance in order to trigger the BaseClass static constructor call).

Any suggestion on what to do to achieve this? I accept suggestion on other ways to achieve something similar (i.e. keep a dictionary generated at runtime with instances of my classes), the subclasses being singletons isn't critical, so maybe I could register constructors instead of instances in the dictionary? I have no idea if that is even possible, with each constructor returning a different type.

Uretki
  • 197
  • 9
  • FYI: [`Attributes`](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/). What you have there is a static [`field`](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/fields) (`T instance`) and a static [`property`](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties) `T instance => instance;` – ProgrammingLlama Feb 21 '22 at 10:33
  • What is the actual goal? As I understand it, you would have some mutable static data, and that can be error prone. Some IOC containers may provide features like constructing all types for some specific interface. – JonasH Feb 21 '22 at 10:54
  • @JonasH The goal is to be able to get instances of those classes based on some values whose combinations is unique, without having to manually modify some factory method and without having to manually trigger registration in some dictionary every time a new subclass is added. – Uretki Feb 21 '22 at 11:03
  • So what I want/need is something that would allow me to do some sort of "dynamically managed factory method". Every instance of the class will have the same attributes, hence why my current (bad) attempt at pulling it off is just turning them into singletons and storing them in a dictionary. – Uretki Feb 21 '22 at 11:07
  • That does not describe *why* you want a dictionary of such classes. It can be useful to provide some more background information to prevent X/Y problems. – JonasH Feb 21 '22 at 13:49
  • @JonasH It's complex, but I have several files which need to be managed in specific ways depending on the file, containing properties, each belonging to one of (currently) two kinds of "structures" (not in a programmatical sense), each with their specificities, some files *may* serve the same purpose in the two structures while some files are exclusive to one structure, those files *may* be further declined with some other field, and the behavior there again can change. (cont.) – Uretki Feb 21 '22 at 14:19
  • Each Subclass in my post correspond to a *specific* type of file, and the dictionary here helps retrieve the files that are, relevent for a specific situation (ex: "I want the file that serves "this" purpose for structure A with the third field as X"), so each subclass is effectively identified by its structure, its purpose and a third field. I've made components such that you can declare a new file if needed (which would only happen very rarely) by declaring its class and "plugging" the right components in the relevent fields when a non-default component is needed. – Uretki Feb 21 '22 at 14:19

1 Answers1

0

You could loop through the types in your assembly and check if they implement the generic class, and then invoke the static constructor of the generic class (which will implicitly invoke the static constructor of the sub class because of Activator.CreateInstance) by calling RuntimeHelpers.RunClassConstructor. So just add a method that does that can call it:

public static void RegisterAll()
{
  foreach(var type in typeof(BaseClass).Assembly.GetTypes().Where(t => t.BaseType.IsGenericType &&
  t.GetGenericTypeDefinition() == typeof(GenericBaseClass<>)))
  {
    RuntimeHelpers.RunClassConstructor(type.BaseType.TypeHandle);
  }
}
Sohaib Jundi
  • 1,576
  • 2
  • 7
  • 15