Статический конструктор регистрирует конструктор объекта => не вызывается

Я хотел использовать статический конструктор в производных классах для регистрации лямбда-выражений конструктора объектов для тех классов, которые будут вызываться при передаче определенного объекта Type.

Поскольку статический конструктор вызывается только непосредственно перед созданием первого объекта этого класса, НО я хочу использовать результат статического конструктора, чтобы определить, какой объект создавать, этот механизм не работает, лямбда-конструктор не найден.

Есть ли выход из этого?

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));

person oliver    schedule 17.03.2019    source источник
comment
Есть ли выход из этого? - В принципе, я бы попробовал найти другой подход. Точная зависимость от того, когда будут выполняться статические конструкторы, сделает код трудным для понимания. Почему бы просто не иметь отдельный метод, который вы вызываете явно?   -  person Jon Skeet    schedule 17.03.2019
comment
(Обратите внимание, что статические конструкторы вызываются в других ситуациях, например, когда статические методы вызываются для типа. Не обязательно создавать экземпляр. Но использование типа в качестве аргумента универсального типа не вызвать статический конструктор, как вы заметили.)   -  person Jon Skeet    schedule 17.03.2019
comment
Ну, цель этого механизма состояла в том, чтобы держать вызов регистрации рядом с определением соответствующего класса (в данном случае SeriesViewForm). Поэтому, если мне нужно добавить еще один класс, производный от CacheableViewForm, я знаю, что должен добавить этот вызов в этот класс. Если регистрация выполнена, например. в моей MainForm, тогда мне нужно будет не забыть внести изменения в два разных места (производный класс MainForm И CacheableViewForm), чтобы расширить все это.   -  person oliver    schedule 17.03.2019
comment
для вашей информации статический конструктор не принимает модификаторы доступа и не имеет параметров. Статический конструктор вызывается автоматически для инициализации класса перед созданием первого экземпляра или обращением к любым статическим членам. Статический конструктор нельзя вызвать напрямую   -  person sayah imad    schedule 17.03.2019
comment
@sayahimad: да, я знал это, спасибо.   -  person oliver    schedule 17.03.2019
comment
Да, это неидеально, но работает. Другой подход состоит в том, чтобы использовать атрибуты для связывания соответствующих классов вместе, а затем анализировать их с отражением при запуске.   -  person Jon Skeet    schedule 17.03.2019


Ответы (1)


Мое решение состоит в том, чтобы перенести инициализацию (из статического конструктора производных классов) в статический конструктор базового класса (который вызывается автоматически перед выполнением моего первого вызова метода CreateFromTargetType) и использовать там отражение для проверки производных классов. В производных классах я определил статические методы (TargetType), которые возвращают конкретные Type, над которыми они работают.

Ни элегантно, ни компактно, и, конечно, не супер быстро, но это позволяет мне поддерживать связь между производным классом (например, SeriesViewForm) и типом данных, с которым он работает (typeof(Series)), близко друг к другу (в одном и том же определении класса).

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);}

    // ...
}
person oliver    schedule 17.03.2019