← Назад к вопросам

Для каких классов нельзя использовать конструктор

2.0 Middle🔥 142 комментариев
#C# и ООП

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Когда в Unity нельзя использовать конструктор

В Unity существуют определенные классы, создание экземпляров которых через конструктор (new MyClass()) является неправильным, нежелательным или даже приведет к ошибкам. Это связано с архитектурой движка, который управляет жизненным циклом объектов.

1. Классы, наследующие от MonoBehaviour

Это самый главный и распространенный случай. Все скрипты, которые вы цепляете на GameObject, наследуются от MonoBehaviour. Создание таких объектов через new строго запрещено, так как это нарушит внутреннюю логику Unity.

Причины:

  • Unity управляет их жизненным циклом, вызывая специальные методы (Awake, Start, Update и т.д.). При создании через new эти методы не будут вызваны.
  • Объекты MonoBehaviour должны быть привязаны к GameObject и иметь контекст сцены. Конструктор этого не обеспечивает.
  • Они могут содержать ссылки на компоненты, другие объекты сцены, которые инициализируются на этапе загрузки.

Правильный способ создания: Использовать GameObject.AddComponent<T>().

// Неправильно! Так делать НЕЛЬЗЯ:
MyScript script = new MyScript();

// Правильно:
GameObject newGO = new GameObject("MyObject");
MyScript correctScript = newGO.AddComponent<MyScript>();

2. Классы, наследующие от ScriptableObject

ScriptableObject — это контейнеры данных, часто используемые для создания ресурсов (assets) в редакторе. Их тоже нельзя создавать конструктором.

Причины:

  • Они должны создаваться через систему ассетов Unity для корректной работы с редактором, сериализацией и управлением памятью.

Правильный способ создания: Использовать статический метод ScriptableObject.CreateInstance<T>().

// Неправильно:
MyScriptableObject so = new MyScriptableObject();

// Правильно:
MyScriptableObject correctSO = ScriptableObject.CreateInstance<MyScriptableObject>();
// Далее его можно сохранить как Asset в папку проекта

3. Объекты, связанные с нативной (C++) стороной Unity

Некоторые классы в Unity являются обертками над нативными C++ объектами. Их создание через конструктор либо невозможно (конструктор приватный), либо бессмысленно.

  • Texture2D, Material, Mesh, Shader и другие ресурсы: Они создаются через статические методы или методы фабрики (например, new Texture2D(width, height) — это корректно, но внутри это сложный процесс, а не просто вызов конструктора; для многих ресурсов предпочтительнее создание в редакторе или через Resources.Load/адресаемую систему).
  • Некоторые служебные классы, такие как GUIStyleState, могут иметь приватные конструкторы. Их экземпляры возвращаются свойствами других объектов.

4. Когда конструктор может помешать работе инверсии управления (IoC) и внедрения зависимостей (DI)

Хотя это не специфично для Unity, при использовании фреймворков для DI (например, в составе Unity Extenject (Zenject) или с использованием IServiceProvider в новом Input System) прямое использование new для сервисов может обойти контейнер зависимостей. Это лишит вас преимуществ DI, таких как автоматическое разрешение зависимостей, контроль времени жизни и мокабильность для тестов.

Плохая практика (в контексте DI):

public class PlayerController : MonoBehaviour
{
    private IWeaponService _weaponService;

    void Start()
    {
        // Прямое создание, зависимости не внедряются.
        _weaponService = new WeaponService();
    }
}

Лучшая практика: позволить DI-контейнеру внедрить зависимость через конструктор MonoBehaviour (если фреймворк это поддерживает) или через поле/метод.

public class PlayerController : MonoBehaviour
{
    [Inject] // Атрибут, например, из Zenject
    private IWeaponService _weaponService;
}

5. Классы, которые являются частью внутренней архитектуры проекта

Часто на проекте вводятся менеджеры, сервисы, репозитории, которые являются синглтонами или имеют сложную инициализацию. Использование new для них в случайных местах приводит к нарушению контроля над их жизненным циклом и может вызвать ошибки (например, попытку создать второй экземпляр синглтона).

Рекомендуемый подход: Использовать фабричные методы, статические свойства или DI-контейнер.

// Вместо этого:
GameManager manager = new GameManager();

// Можно так (пример простого синглтона с ленивой инициализацией):
public class GameManager : MonoBehaviour
{
    private static GameManager _instance;
    public static GameManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<GameManager>();
                if (_instance == null)
                {
                    GameObject go = new GameObject("GameManager");
                    _instance = go.AddComponent<GameManager>();
                    DontDestroyOnLoad(go);
                }
            }
            return _instance;
        }
    }
}
// А затем использовать:
GameManager correctManager = GameManager.Instance;

Общий вывод и рекомендации

  • Всегда помните о MonoBehaviour и ScriptableObject — для них есть специальные API создания.
  • Соблюдайте архитектурные паттерны проекта. Если в проекте используется DI или синглтоны, не создавайте объекты в обход этих правил.
  • Читайте документацию. Если класс является частью Unity API, проверьте, есть ли у него публичный конструктор и рекомендуется ли его использовать.
  • Конструктор MonoBehaviour использовать технически можно, но это почти всегда ошибка. Unity может вызывать его в неожиданные моменты (например, при десериализации), что ведет к трудноотлаживаемым багам. Вся инициализация должна проводиться в Awake() или Start().

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