Есть ли способ заставить статические поля быть инициализированными в C #? - PullRequest
7 голосов
/ 28 января 2010

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

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, которое возвращает все экземпляры указанного типа.

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

РЕДАКТИРОВАТЬ: ОБРАЗЕЦ ВЫШЕ ПРОВЕРЕНО, ПОПРОБУЙТЕ ЕГО ОДИН:

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 ]

7 голосов
/ 01 февраля 2010

Ответ на ваш вопрос «хорошо, да». Но один из двух способов «принуждения» - это то, что вы уже делаете.

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

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

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

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

6 голосов
/ 27 августа 2015

В этом случае существует способ принудительной инициализации свойств. Изменение требует добавления параметра типа в базовый класс для представления будущего подкласса базового класса, который будет содержать поля для инициализации. Затем мы можем использовать 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

4 голосов
/ 28 января 2010

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

т.е. вам нужно изменить:

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

до:

* +1007 *
2 голосов
/ 28 января 2010

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

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

красивые печатает больше строк

0 голосов
/ 02 июля 2015

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

Синтаксис

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

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

...