Просто идея, вы можете сделать что-то вроде этого:

ментация по статическим конструкторам в C # говорит:Статический конструктор используется для инициализации любых статических данных или для выполнения определенного действия, которое необходимо выполнить только один раз. Вызывается автоматически

до создания первого экземпляра или ссылки на статические элементыЭта последняя часть (о том, когда он вызывается автоматически) заставила меня задуматься; пока не прочитал эту часть I.

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

статический членыполученный класс, что тогда?Чтобы сделать это немного конкретнее, я

подумал что код ниже будет работать:Я предположил, что доступ к

abstract class TypeBase
{
    static TypeBase()
    {
        Type<int>.Name = "int";
        Type<long>.Name = "long";
        Type<double>.Name = "double";
    }
}

class Type<T> : TypeBase
{
    public static string Name { get; internal set; }
}

class Program
{
    Console.WriteLine(Type<int>.Name);
}

 класс будет автоматически вызывать статический конструктор дляType<T>; но, похоже, дело не в этом.TypeBase являетсяType<int>.Nameи код выше выводит пустую строку.nullПомимо создания некоторого фиктивного члена (как статический

 метод, который ничего не делает),Initialize()Есть ли лучший способ гарантировать, что статический конструктор базового типа будет вызываться перед использованием любого из его производных типов?Если нет, то ... это пустышка!

Если это не пример кода только ради вопроса, не могли бы вы просто сделать

 Dan Tao11 янв. 2011 г., 00:14
Используя абстрактный класс в качестве конкретного примера? ;-)
 Matt Greer11 янв. 2011 г., 00:07
@Matt: Извините, я должен был это прояснить, вы правы: это пример кода только ради вопроса.public static string Name { get { return typeof(T).Name; } }?
 Geeb06 окт. 2017 г., 11:14
Правила здесь

Ответы на вопрос(6)

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

System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof (TypeBase).TypeHandle);

+1 Это похоже на работу. Как и в случае любого статического решения порядка инициализации, я сомневаюсь, является ли он пуленепробиваемым, но, по крайней мере, он не запускает статический ctor дважды, если вы вызываете его дважды, в простом однопоточном приложении.

 Tod Thomson30 янв. 2014 г., 09:00
Как отметили другие, ваш анализ верен. Спецификация реализована здесь буквально; поскольку ни один член базового класса не был вызван, а экземпляр не был создан, статический конструктор базового класса не вызывается. Я могу видеть, как это может быть удивительно, но это строгая и правильная реализация спецификации.
 Merlyn Morgan-Graham11 янв. 2011 г., 04:39
Есть идеи о производительности этого метода? Использует ли он отражение и / или это быстро, если статический конструктор уже был вызван?
 devios122 февр. 2011 г., 05:27
Это способ сделать это - для репликации base () в статических конструкторах просто вызовите RuntimeHelpers. RunClassConstructor (TypeOf (TypeBase) .TypeHandle); в начале каждого конструктора.

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

Это печатает «B», несмотря на то, что «член D» был вызван. М является членом D исключительно по наследству; CLR не может различить, вызван ли B.M «через D» или «через B».

class Program 
{
  static void Main(string[] args)
  {      
    D.M();
  }      

}
class B 
{ 
  static B() { Console.WriteLine("B"); }
  public static void M() {}
} 
class D: B 
{ 
  static D() { Console.WriteLine("D"); }
}

Во всем моем тестировании я смог получить только вызов фиктивного члена на базе, чтобы заставить базу вызывать свой статический конструктор, как показано на рисунке:

й типизированный файл, который выполнял следующие действия:

class Base
{
    static Base()
    {
        Console.WriteLine("Base static constructor called.");
    }

    internal static void Initialize() { }
}

class Derived : Base
{
    static Derived()
    {
        Initialize(); //Removing this will cause the Base static constructor not to be executed.
        Console.WriteLine("Derived static constructor called.");
    }

    public static void DoStaticStuff()
    {
        Console.WriteLine("Doing static stuff.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Derived.DoStaticStuff();
    }
}

Однако это похоже на хак (хотя и на фиктивный член) только для того, чтобы вызвать базовый статический конструктор для вызова.

private static readonly Base myBase = new Base();

Да, да, это так ... но может быть, это единственный способ?

 Dan Tao11 янв. 2011 г., 00:15
Да, похоже, единственный путь. Помимо вашего решения и фиктивного статического поля, которое создает основу, я не могу найти других способов сделать это.
 Joshua Rodgers11 янв. 2011 г., 00:17
Я почти всегда жалею, что полагаюсь на что-то подобное. Статические методы и классы могут ограничить вас позже. Если вы захотите написать какое-то специальное поведение для вашего класса Type позже, вы будете помещены в коробку.

зового кл

    abstract class TypeBase
    {
        static TypeBase()
        {
            Type<int>.Name = "int";
            Type<long>.Name = "long";
            Type<double>.Name = "double";
        }
    }

    class Type<T> : TypeBase
    {
        static Type() 
        {
            new Type<object>();
        }

        public static string Name { get; internal set; }
    }

    class Program
    {
        Console.WriteLine(Type<int>.Name);
    }

вот небольшой вариант вашего подхода. Это немного больше кода, но он позволит вам иметь определенный тип, определенный позже, который позволяет вам делать пользовательские вещи.

Позже, когда вы добавите виртуальный метод в Type и захотите специальную реализацию для Type, вы можете реализовать таким образом:

    abstract class TypeBase
    {
        private static bool _initialized;

        protected static void Initialize()
        {
            if (!_initialized)
            {
                Type<int>.Instance = new Type<int> {Name = "int"};
                Type<long>.Instance = new Type<long> {Name = "long"};
                Type<double>.Instance = new Type<double> {Name = "double"};
                _initialized = true;
            }
        }
    }

    class Type<T> : TypeBase
    {
        private static Type<T> _instance;

        public static Type<T> Instance
        {
            get
            {
                Initialize();
                return _instance;
            }
            internal set { _instance = value; }
        }

        public string Name { get; internal set; }
    }

А затем подключите его, изменив

class TypeInt : Type<int>
{
    public override string Foo()
    {
        return "Int Fooooo";
    }
}

Мой совет - избегать статических конструкторов - это легко сделать. Также избегайте статических классов и, где возможно, статических членов. Я не говорю никогда, просто экономно. Предпочитаю синглтон класса статическому.

protected static void Initialize()
{
      if (!_initialized)
      {
          Type<int>.Instance = new TypeInt {Name = "int"};
          Type<long>.Instance = new Type<long> {Name = "long"};
          Type<double>.Instance = new Type<double> {Name = "double"};
          _initialized = true;
       }
}

+1: хотя это не решает конкретную проблему, я думаю, что это хороший совет.

 TrueWill19 янв. 2011 г., 22:55
Просто идея, вы можете сделать что-то вроде этого:
 Merlyn Morgan-Graham11 янв. 2011 г., 04:36
... и предпочитаю контейнеры IoC синглетонам. :)
Решение Вопроса

и между CLR 2.0 и CLR 4.0 они на самом делеизменилось тонкими и интересными способамиТо, что IMO делает наиболее "умные" подходы хрупкими между версиями CLR. методInitialize()также может не выполнять работу в CLR 4.0, если она не касается полей.Я бы искал альтернативный дизайн, или, возможно, использовать

регулярный Ленивая инициализация в вашем типе (т. е. проверить бит или ссылку (против) чтобы увидеть, если это было сделано).nullМне удалось позвонить фиктивному элементу Initialize (), который не касался полей с помощью CLR 4.0. Согласно коду я разместил в своем ответе.

 Erik Philips16 нояб. 2016 г., 23:27
Вы можете вызвать статический конструктор explicity, поэтому вам не нужно создавать какие-либо методы для инициализации:
 Joshua Rodgers11 янв. 2011 г., 00:16
@Joshua: Пока у базового класса есть статический конструктор - не только инициализаторы полей - тогда вызывается
 LukeH11 янв. 2011 г., 00:45
было
 LukeH11 янв. 2011 г., 00:42
являетсяInitialize  гарантированно вызовет этот конструктор, потому что тип не будет помечен, Как сказано в спецификации, будет вызван статический конструкторbeforefieldinit«... до создания первого экземпляра или ссылки на любые статические элементы», И поскольку вопрос состоит в том, как заставить статический конструктор сработать, мы можем предположить, что он будет.Сказав это, полагаться на неясные углы спецификации для гарантии функциональности не очень хорошая идея, особенно когда кто-то незнакомый с этим углом спецификации приходит, чтобы обновить код через несколько лет. Намного лучше сделать что-то явное и заведомо правильное, как вы предлагаете.

Ваш ответ на вопрос