Как вызвать статический конструктор - PullRequest
6 голосов
/ 02 августа 2011

код:

class Base<T,U> where T:Base<T,U>,new()  where U :class
{
    protected static U _val = null;
    internal static void ShowValue()
    {
        if(_val == null)new T(); //Without this line, it won't work as expected
        Console.WriteLine (_val);
    }
    internal static void Virtual()
    {
        Console.WriteLine ("Base");
    }
}
class Deriv :Base<Deriv,string>
{
    static Deriv()
    {
        _val = "some string value";
    }
    internal static new void Virtual ()
    {
        Console.WriteLine ("Deriv");
    }
}
 public static void Main (string[] args)
{
    Deriv.ShowValue();            
    Deriv.Virtual();
}

Благодаря универсальным типам .NET я могу создать группу конкретных классов, повторно используя универсальные статические методы, определенные в базовом базовом классе.Это может до некоторой степени имитировать наследственный полиморфизм.Но чтобы инициализировать другую версию статических полей, я должен использовать статические конструкторы.К сожалению, мы не можем вызвать их напрямую, поэтому нам нужно найти способ вызвать его вызов.Приведенный выше пример показал путь.Но мне не нравится ни инстанциация, ни рефлексивный подход.Мы также не можем наложить ограничение на статический метод универсального параметра.Итак, я хотел бы спросить, есть ли другой способ сделать такую ​​работу!

Заранее спасибо!

~~~~~~~~~~~~~~~~

Некоторые выводы (Возможно, немного рано):

Кажется, что нет никакого обходного пути, чтобы справиться с такой ситуацией.Я должен создать экземпляр подкласса или использовать отражение.Учитывая, что .cctors нужно просто вызывать один раз, я предпочитаю подход отражения, потому что в некоторых случаях ограничение new () просто не является выбором - как будто вы не должны предоставлять ctor без параметров пользователю.

После проведения дальнейшего эксперимента я обнаружил, что .cctors можно вызывать несколько раз, но только первый вызов повлияет на установку статических полей.Это странно, но хорошо странно!

    class MyClass 
    {
        static int _val = 0;
        static MyClass()
        {
            _val++;
            Console.WriteLine (_val);
        }
    }
    public static void Main (string[] args)
    {
        ConstructorInfo ci = typeof(MyClass).TypeInitializer;
        ci.Invoke(new object[0]);
        ci.Invoke(new object[0]);
        ci.Invoke(new object[0]);
    }
//result:
//1
//1
//1
//1

Ответы [ 5 ]

6 голосов
/ 02 августа 2011

Я бы настоятельно рекомендовал вам переосмыслить свой дизайн.Попытка использовать этот тип обходного пути для «статического наследования» ведет к борьбе с некоторыми из основных конструкций .NET.

Неясно, какую большую проблему вы пытаетесь решить, но пытаетесь «умный» код, подобный этомусимуляция наследования приведет к коду, который очень сложно поддерживать и диагностировать в долгосрочной перспективе.

Без на самом деле с использованием члена Deriv (или создания его экземпляра),вы в основном не будете запускать статический конструктор.Важно понимать, что Deriv.ShowValue() в основном преобразуется в вызов

Base<Deriv, string>.ShowValue();

... так что вы не на самом деле , который вызывает что-либо на Deriv.Ваш вызывающий код был бы на самом деле более понятным, если бы он был написан таким образом.

РЕДАКТИРОВАТЬ: Еще одна (явно неудачная) причина, по которой следует избегать явного использования инициализаторов типов, заключается в том, что в .NET 4.5 есть ошибка, которая приводит к исключениювыбрасывается неуместно в некоторых случаях.См. мой вопрос по теме для получения дополнительной информации.

2 голосов
/ 16 ноября 2012

Правильное решение - вызвать инициализатор типа (= статический конструктор) следующим образом:

typeof(T).TypeInitializer.Invoke(null, null);

Требуется оба null s.Указание только одного дает MemberAccessException.

Таким образом, ваш код может выглядеть примерно так:

internal static void ShowValue()
{
    if (_val == null)
    {
        if (typeof(T).TypeInitializer != null)
            typeof(T).TypeInitializer.Invoke(null, null);
        if (_val == null)
            throw new InvalidOperationException(string.Format("The type initializer of {0} did not initialize the _val field.", typeof(T)));
    }
    Console.WriteLine(_val);
}

И с этим вы можете удалить ограничение new().

2 голосов
/ 02 августа 2011

Вы не можете контролировать, когда статический конструктор будет выполняться, но гарантируется, что он будет работать до получения доступа к любому статическому свойству или методу и до создания экземпляра.

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

0 голосов
/ 02 августа 2011

Я направляю вас к статье MSDN Статические конструкторы и примерно на 10% вниз по странице:

Статический конструктор вызывается автоматически для инициализации класса до создания первого экземпляра или любых статических элементов ссылка.

0 голосов
/ 02 августа 2011

Статические конструкторы автоматически, только один раз. Вы не можете назвать их сами.

Пример отсюда:

public class Bus
{
    // Static constructor:
    static Bus()
    {
        System.Console.WriteLine("The static constructor invoked.");
    }    

    public static void Drive()
    {
        System.Console.WriteLine("The Drive method invoked.");
    }
}

class TestBus
{
    static void Main()
    {
        Bus.Drive();
    }
}

выход:

The static constructor invoked.

The Drive method invoked.
...