C # Unity> Статический экземпляр экземпляра, не вызывающий конструктор для вызова - PullRequest
0 голосов
/ 01 сентября 2018

Я заметил довольно странное поведение в своем приложении, которое я создаю;

У меня есть определенный класс, в котором есть статическая переменная типа «экземпляр» типа класса.

Я бы предположил, что (согласно приложенному коду) будет вызван конструктор.

Увы, это не так, если я не использую Void.get в нестатическом поле где-либо в моем коде.

public class Void : TilePrototype {

public static Tile get = new Tile((int)TileEntities.Void);
public static Void instance = new Void();

public Void() {
    Debug.Log("created");
    id = (int)TileEntities.Void;
    isBlocking = true;
    register();
}

public override RenderTile render(Tile tile){
    return new RenderTile(0, new Color(0, 0, 0, 0));
}

Так что, когда у меня есть что-то вроде:

    public static TileStack empty = new TileStack(Void.get, Void.get); 

конструктор класса Void никогда не вызывается. Но если у меня есть:

Tile t = Void.get;

Где-нибудь в моем коде это будет называться.

Почему?

Спасибо.

Ответы [ 2 ]

0 голосов
/ 03 сентября 2018

Благодаря ответу Марка Гравелла я придумал эту концепцию (и, по общему признанию, новое решение мне нравится больше, чем старое, поэтому еще раз спасибо!)

Изменения, внесенные в класс Void:

public class Void : TilePrototype {
public static Void instance = new Void();
public static Tile get {
    get {
        return new Tile(instance.id);
    }
}

public Void() {
    isBlocking = true;
}

public override RenderTile render(Tile tile){
    return new RenderTile(0, new Color(0, 0, 0, 0));
}
}

Итак, как вы можете видеть, я сделал переменную "get" свойством, чтобы она оценивалась позже, когда вам действительно нужен тайл, а не на строительстве. Я изменил все "get" таким образом.

Второе изменение в TilePrototype:

public class TilePrototype {
public static Dictionary<int, TilePrototype> tilePrototypeDictionary = new Dictionary<int, TilePrototype>();

public static void registerPrototype(int id, TilePrototype tp){
    tp.id = id;
    tilePrototypeDictionary.Add(id, tp);
}

public static bool registered = false;

public static void registerAll(){
    if( registered ) return;
    registerPrototype(0, Void.instance);
    registerPrototype(1, Air.instance);
    registerPrototype(2, Floor.instance);
    registerPrototype(3, Wall.instance);

(...)

Здесь я добавил функции registerPrototype и registerAll.

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

У меня также есть все зарегистрированные вещи в одном месте, с возможностью добавления во время выполнения еще

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

Смена идентификатора проста и в одном месте и везде, доступ к этому идентификатору осуществляется через короткий .instance.id

Еще раз спасибо за помощь:)

0 голосов
/ 02 сентября 2018

Это действительно очень тонкая и тонкая область C #; По сути, вы наткнулись на «beforefieldinit» и разницу между статическим конструктором и инициализатором типов. Вы можете разумно спросить «когда запускается статический конструктор?», И MSDN скажет вам :

Он вызывается автоматически перед созданием первого экземпляра или ссылками на статические элементы.

Кроме ... public static TileStack empty = new TileStack(Void.get, Void.get); не является статическим конструктором! Это инициализатор статического поля. И , у которого есть другие правила , которые в основном гласят: «Я побегу, когда должен, не позже, возможно раньше». Чтобы проиллюстрировать это на примере: следующий не будет (вероятно) запускать ваш код, потому что он не должен - нет ничего требующего поля:

class Program
{
    static void Main()
    {
        GC.KeepAlive(new Foo());
    }
}

public class Foo
{
    public static TileStack empty = new TileStack(Void.get, Void.get);
}

Однако, если мы сделаем крошечный твик:

public class Foo
{
    public static TileStack empty = new TileStack(Void.get, Void.get);
    static Foo() { } // <=== added this
}

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

Без этого инициализатор статического поля можно отложить до тех пор, пока что-то не коснется статических полей . Если какой-либо из ваших кодов действительно коснется empty, , тогда запустит инициализатор статического поля, и экземпляр будет создан. Значение: также будет иметь такой эффект:

class Program
{
    static void Main()
    {
        GC.KeepAlive(Foo.empty);
    }
}

public class Foo
{
    public static TileStack empty = new TileStack(Void.get, Void.get);
}

Эта способность откладывать выполнение статической инициализации до тех пор, пока статические поля не будут фактически затронуты , называется "beforefieldinit", и она включена, если у типа есть инициализатор статического поля, но нет статического конструктора. Если «beforefieldinit» не включен, то применяется логика «до создания первого экземпляра или ссылки на любые статические элементы».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...