Static constructor registers object constructor => not called

124 Views Asked by At

I wanted to use a static constructor in derived classes to register object constructor lambdas for those classes to be called when passing a certain Type object.

Since a static constructor is only called just before the first object of that class is created BUT I want to use the result of the static constructor to determine what kind of object to create, this mechanism fails, the constructor lambda is not found.

Is there any way out of this?

public abstract class Cacheable
{
    // ... details do not matter ...
}

public class Series: Cacheable
{
    // ... details do not matter ...
}

public abstract class CacheableViewForm
{
    static Dictionary<Type, Func<CacheableViewForm>> cacheableViewFormConstructors = new Dictionary<Type, Func<CacheableViewForm>>();

    protected static void Register<CacheableViewFormClass, CacheableClass>()
        where CacheableViewFormClass: CacheableViewForm, new()
        where CacheableClass: Cacheable
    {
        cacheableViewFormConstructors[typeof(CacheableClass)] = (() => new CacheableViewFormClass());
    }

    public static CacheableViewForm CreateFromTargetType(Type cacheableType)
    {
        return cacheableViewFormConstructors[cacheableType]();
    }

    // ... details do not matter ...
}

public class SeriesViewForm: CacheableViewForm
{
    static SeriesViewForm() {Register<SeriesViewForm, Series>();}

    // ... details do not matter ...
}

// fails because CacheableViewForm.Register<> has not been executed yet!
CacheableViewForm newForm = CacheableViewForm.CreateFromTargetType(typeof(Series));
1

There are 1 best solutions below

0
oliver On

My solution is to move initialization (from the static constructor of the derived classes) to the static constructor of the base class (which gets called automatically before executing my first call to the CreateFromTargetType method) and using reflection in there to examine the derived classes. In the derived classes I defined static methods (TargetType) which return the specific Type they are operating on.

Neither elegant nor compact, and certainly not super fast, but it allows me to keep the association between derived class (e.g. SeriesViewForm) and the data type it operates on (typeof(Series)) close together (in the same class definition).

public abstract class CacheableViewForm: Form
{
    static Dictionary<Type, Func<CacheableViewForm>> CacheableViewFormConstructors = new Dictionary<Type, Func<CacheableViewForm>>();

    static CacheableViewForm()
    {
        var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();

        foreach(Assembly assembly in allAssemblies)
        {
            Type[] cacheableViewFormTypes = assembly.GetExportedTypes().Where(t => typeof(CacheableViewForm).IsAssignableFrom(t) && t != typeof(CacheableViewForm)).ToArray();

            foreach (Type cacheableViewFormType in cacheableViewFormTypes)
            {
                MethodInfo mi = cacheableViewFormType.GetMethod("TargetType");
                Type cacheableType = (Type)mi.Invoke(null, null);
                Func<CacheableViewForm> ctorDelegate = (() => (CacheableViewForm)(Activator.CreateInstance(cacheableViewFormType)));
                CacheableViewFormConstructors[cacheableType] = ctorDelegate;
            }
        }
    }

    public static CacheableViewForm CreateFromTargetType(Type cacheableType)
    {
        return CacheableViewFormConstructors[cacheableType]();
    }
}

public class SeriesViewForm: CacheableViewForm
{
    public static Type TargetType() {return typeof(Series);}

    // ...
}