Есть ли способ заставить статические поля инициализироваться в C#?

рассмотрим следующий код:

class Program
{
    static Program() {
        Program.program1.Value = 5;
    }

    static List<Program> values = new List<Program>();
    int value;
    int Value
    {
        get { return value; }
        set { 
            this.value = value;
            Program.values.Add(this);
        }
    }

    static Program program1 = new Program { value = 1 };
    static Program program2 = new Program { value = 2 };
    static Program program3 = new Program { value = 3 };

    static void Main(string[] args)
    {
        if (Program.values.Count == 0) Console.WriteLine("Empty");
        foreach (var value in Program.values)
            Console.WriteLine(value.Value);
        Console.ReadKey();
    }
}

он печатает только число 5, и если удалить код в статическом конструкторе, он печатает "пустой".

есть ли способ заставить статические поля инициализироваться, даже если они еще не используются?

мне нужно иметь статическое свойство с именем Values с возвратами всех экземпляров указанного типа.

Я пробовал некоторые вариации этого кода и некоторые работы для некоторых типов, но не для другие.

EDIT: ОБРАЗЕЦ ВЫШЕ СЛОМАН, ПОПРОБУЙТЕ ЭТОТ:

class Subclass<T> {
    static Subclass()
    {
        Values = new List<Subclass<T>>();
    }
    public Subclass()
    {
        if (!Values.Any(i => i.Value.Equals(this.Value)))
        {
            Values.Add(this);
        } 
    }

    public T Value { get; set; }

    public static List<Subclass<T>> Values { get; private set; }
}

class Superclass : Subclass<int>
{
    public static Superclass SuperclassA1 = new Superclass { Value = 1 };
    public static Superclass SuperclassA2 = new Superclass { Value = 2 };
    public static Superclass SuperclassA3 = new Superclass { Value = 3 };
    public static Superclass SuperclassA4 = new Superclass { Value = 4 }; 
}

class Program
{
    static void Main(string[] args)
    {
        //Console.WriteLine(Superclass.SuperclassA1); //UNCOMMENT THIS LINE AND IT WORKS
        foreach (var value in Superclass.Values)
        {
            Console.WriteLine(value.Value);
        }
        Console.ReadKey();
    }
}

5 ответов


ответ на ваш вопрос: "Ну, да". Но один из двух способов "заставлять" это то, что вы уже делаете.

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

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

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

Если класс содержит метод Main (раздел 3.1), в котором начинается выполнение, статический конструктор для этого класса выполняется до вызова метода Main. Если класс содержит статические поля с инициализаторами, эти инициализаторы выполняются в текстовом порядке непосредственно перед выполнением статического конструктора."


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

т. е. нужно менять:

    static Program program1 = new Program { value = 1 };
    static Program program2 = new Program { value = 2 };
    static Program program3 = new Program { value = 3 };

в:

    static Program program1 = new Program { Value = 1 };
    static Program program2 = new Program { Value = 2 };
    static Program program3 = new Program { Value = 3 };

на самом деле есть способ принудительно инициализировать свойства в этом случае. Изменение требует добавления параметра type в базовый класс для представления будущего подкласса базового класса, который будет содержать поля для инициализации. Тогда мы можем использовать RuntimeHelpers.RunClassConstructor для обеспечения инициализации статических полей подкласса.

следующий даст результаты, которые вы ищете:

class Subclass<TSubclass, T> 
{
    static Subclass()
    {
        Values = new List<Subclass<TSubclass, T>>();
        // This line is where the magic happens
        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(TSubclass).TypeHandle);
    }
    public Subclass()
    {
        if (!Values.Any(i => i.Value.Equals(this.Value)))
        {
            Values.Add(this);
        } 
    }

    public T Value { get; set; }

    public static List<Subclass<TSubclass, T>> Values { get; private set; }
}

class Superclass : Subclass<Superclass, int>
{
    public static Superclass SuperclassA1 = new Superclass { Value = 1 };
    public static Superclass SuperclassA2 = new Superclass { Value = 2 };
    public static Superclass SuperclassA3 = new Superclass { Value = 3 };
    public static Superclass SuperclassA4 = new Superclass { Value = 4 }; 
}

public class Program
{
    public static void Main()
    {
        foreach (var value in Superclass.Values)
        {
            Console.WriteLine(value.Value);
        }
        Console.ReadKey();
    }
}

что происходит это вызов RuntimeHelpers.RunClassConstructor(typeof(TSubclass).TypeHandle) заставляет статический конструктор TSubclass выполнить, если он еще не запущен. Это гарантирует, что статические поля были инициализированы сначала в соответствии с этой строкой изhttps://msdn.microsoft.com/en-us/library/aa645612 (VS.71).aspx:

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

здесь dotnetfiddle демонстрирует его работу:

https://dotnetfiddle.net/MfXzFd


на самом деле выглядит, что вы неправильно написали "значение" - > "значение" Итак:

    static Program program1 = new Program { Value = 1 };
    static Program program2 = new Program { Value = 2 };
    static Program program3 = new Program { Value = 3 };

pretty печатает больше строк


второй пример не работает просто потому, что Value является статическим членом Subclass.

синтаксис C# позволяет Superclass.Values, но в конечном итоге скомпилированный вызов метода будет в Subclass.Values геттер. Итак, тип Superclass на самом деле никогда не трогали. Superclass.SuperclassA1 С другой стороны, касается типа и запускает статическую инициализацию.

вот почему C# на самом деле не имеет неявной статической инициализации, и вам нужны структуры композиции, такие как MEF и Unity.