Могу ли я убедиться, что статический конструктор завершил работу до создания экземпляра? - PullRequest
0 голосов
/ 12 июня 2019

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

class Foo {
  private static List<string> list = new List<string>()
  private static object listLock = new Object();

  public Foo(string s) {
    lock(listLock)
      list.Add(s);
  }
}

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

Есть ли какой-то способ убедиться, что конструкторвызывается только после того, как закончился статический (кроме уродливых хаков типа while(list == null){})?

Ответы [ 2 ]

3 голосов
/ 12 июня 2019

Вот хорошая ссылка на нюансы before-field-init , но на самом деле: добавление явного статического конструктора должно заставить руку времени выполнения здесь. Обратите внимание, что while (list == null) {} не будет работать , потому что также заставляет руку времени выполнения - вы никогда не сможете наблюдать ложь, которую среда выполнения говорит вам, в основном.

В показанном примере это не имеет значения . Вы никогда не увидите null для listLock или list. Если вы действительно действительно требуете, чтобы они выполнялись перед конструкторами:

private static List<string> list;
private static object listLock;
static Foo() {
    list = new List<string>();
    listLock = new object();
}

Но учтите, что это на самом деле не нужно и может негативно повлиять на ваш код, особенно с новым JIT в .NET Core 3, который может обрабатывать static readonly поля с дополнительным voodoo , если они инициализирован аккуратно знаю он может сделать это, если они являются инициализаторами встроенных полей без явного статического конструктора; я не знаю , если он может сделать это, если они назначены явным статическим конструктором).

0 голосов
/ 13 июня 2019

Я не могу воспроизвести проблему. Программа ниже создает 5 Foo экземпляров параллельно, а конструктор каждого Foo экземпляра использует в качестве блокировки статический экземпляр класса SlowObject, для которого требуется 500 мсек. Тем не менее, SlowObject всегда создается до запуска любого конструктора Foo. Я тестировал эту программу с различными версиями .NET Framework, C # версий 4, 5, 6 и 7, с конфигурациями Debug и Release , с подключенным отладчиком и без него. Выход всегда одинаков. Комментирование статических конструкторов не имеет значения.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;

class Program
{
    static Program()
    {
        Program.ConsolePrint("Program Static Constructor");
    }

    static void Main(string[] args)
    {
        ConsolePrint("Starting Tasks");
        var tasks = Enumerable.Range(1, 5).Select(n => Task.Run(() =>
        {
            new Foo(n.ToString());
        })).ToArray();
        ConsolePrint("Waiting Tasks");
        Task.WaitAll(tasks);
        ConsolePrint("Tasks Finished");
        //Console.WriteLine("Foo.list: " + String.Join(", ", Foo.GetItems()));
    }

    public static void ConsolePrint(string line)
    {
        Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " ["
            + Thread.CurrentThread.ManagedThreadId.ToString() + "] > " + line);
    }
}

public class Foo
{
    static Foo()
    {
        Program.ConsolePrint("Foo Static Constructor");
    }

    private static List<string> list = new List<string>();
    private static object listLock = new SlowObject();

    public Foo(string s)
    {
        Program.ConsolePrint("Creating Foo " + s);
        lock (listLock)
        {
            list.Add(s);
        }
    }
}

public class SlowObject
{
    static SlowObject()
    {
        Program.ConsolePrint("SlowObject Static Constructor");
    }

    public SlowObject()
    {
        Program.ConsolePrint("SlowObject Instance Constructor Started");
        Thread.Sleep(500);
        Program.ConsolePrint("SlowObject Instance Constructor Finished");
    }
}

Выход:

13:40:24.091 [1] > Program Static Constructor
13:40:24.112 [1] > Starting Tasks
13:40:24.131 [1] > Waiting Tasks
13:40:24.132 [3] > SlowObject Static Constructor
13:40:24.133 [3] > SlowObject Instance Constructor Started
13:40:24.635 [3] > SlowObject Instance Constructor Finished
13:40:24.635 [3] > Foo Static Constructor
13:40:24.637 [3] > Creating Foo 1
13:40:24.637 [4] > Creating Foo 2
13:40:24.643 [3] > Creating Foo 5
13:40:24.639 [5] > Creating Foo 3
13:40:24.641 [6] > Creating Foo 4
13:40:24.647 [1] > Tasks Finished
Press any key to continue . . .
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...