🎮🔑 Искусство управления доступом в Unity: модификаторы public, private и protected

Public, private и protected — три ключевых слова, которые определяют уровень доступа к полям и методам в C# и Unity. Делимся секретами, как правильно использовать их, чтобы сделать ваш код безопаснее и понятнее.

Данная статья является переводом другой статьи. С оригиналом вы можете ознакомиться по ссылке.

Одно из первых действий, которое вы, скорее всего, сделаете в скрипте Unity, — это создадите поле. И, скорее всего, сделаете его публичным, как это делают во многих уроках и гайдах.

Например, так:

public float number;

Ключевое слово public в Unity — это модификатор доступа, который определяет уровень доступности поля (или метода, свойства, класса).

В данном случае сделать что-то публичным, означает разрешить другим классам получать к нему доступ.

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

Даже если вы новичок в Unity, модификаторы доступа, как правило, очень просты в использовании.

Далее мы рассмотрим их особенности и области применения.

Объяснение модификаторов доступа в Unity

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

Существует четыре основных типа модификаторов доступа:

  1. Public
  2. Private
  3. Protected
  4. Internal

Вы, вероятно, уже использовали public и private для изменения уровня доступа переменных и функций в своих скриптах.

Protected, в свою очередь, ограничивает доступность типа только классами-наследниками, что может быть для вас новым.

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

Public, вероятно, является модификатором доступа, с которым вы наиболее знакомы.

Public в Unity

Публичные типы не имеют ограничений в области видимости, поэтому могут быть прочитаны и использованы любым другим скриптом.

Чтобы сделать что-то публичным, просто добавьте ключевое слово public при объявлении:

public class ExampleClass : MonoBehaviour
{
    public float number;

    public void AddNumber(float numberToAdd)
    {
        number += numberToAdd;
    }
}

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

public class OtherClass : MonoBehaviour
{
    ExampleClass exampleClass;

    private void Start()
    {
        exampleClass.AddNumber(5);
        Debug.Log(exampleClass.number);
    }
}

Как получить доступ к полю из другого скрипта

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

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

Например:

class ExampleClass : MonoBehaviour
{
    // Этот класс будет Internal по умолчанию
}

Private и protected классы в Unity

Обычно, когда вы создаете класс, вы делаете это непосредственно в глобальном пространстве имен по умолчанию. Хотя разделение частей проекта на различные пространства имён – хорошая и полезная практика, она, однако, не относится к теме данной статьи, а в небольших проектах от этого и вовсе могут отказываться.

В описанном выше случае класс может быть либо Public, либо Internal, что в Unity зачастую означает одно и то же.

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

Например:

class GeneralClass : MonoBehaviour
{
    // Этот класс является Internal по умолчанию

    class LocalClass
    {
        // А этот класс - приватным
    }
}

Это может быть полезно, если вы хотите создать класс как своего рода структуру данных, которую вы собираетесь использовать только в частном порядке в том же скрипте, в котором вы его объявляете.

Поля, помеченные как public, автоматически сериализуются Unity, то есть, если они являются сериализуемым типом (числом, строкой или классом, помеченным атрибутом [Serializable]), они будут отображаться в инспекторе.

Вы можете использовать ключевое слово public, чтобы отобразить значения и ссылки во вкладке Inspector. Однако же, как правило, лучше использовать атрибут [SerializeField], чтобы отобразить значение в инспекторе, оставляя возможность пометить поле как private.

Private в Unity

Приватные поля и методы обеспечивают наименьший уровень доступа и не могут использоваться ничем за пределами тела, в котором они существуют.

Например, приватное поле не может быть прочитано или использовано другим скриптом, даже если у него есть ссылка на класс, в котором это поле существует.

Поля, которые существуют внутри скрипта, обычно являются private по умолчанию:

float numberA; // это поле будет приватным
private float numberB; // и это тоже

Readonly в Unity

Хотя модификатор private может быть полезен для ограничения области видимости поля, существуют дополнительные способы ограничения того, как значение может использоваться независимо от того, является ли оно public или private.

Например, модификатор readonly можно использовать для того, чтобы позволить устанавливать значение полю только в месте его объявления и конструкторе класса. Это означает, что переменная только для чтения может быть установлена ​​только один раз при инициализации.

Если конструкторы для вас в новинку, то они, по сути, являются методами, которые вызываются при создании класса и используются для установки каких-либо начальных значений, необходимых объекту для дальнейшей работы.

Пример использования модификатора readonly:

public readonly int year = 2024;

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

Это может быть значение, которое используется во всем скрипте, но которое не должно изменяться в какой-либо момент, даже классом, который его создал.

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

Один из способов сделать это — использовать автоматически реализуемое свойство, которое объединяет в себе публичный get с частным set:

public float NumberC { get; private set;}

В большинстве случаев можно ограничиться публичными и приватными полями и методами.

Однако есть еще один модификатор доступа, protected, который обеспечивает больший доступ, чем private, но не делает поле или метод полностью публичным.

#️⃣ Библиотека шарписта
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека шарписта»
🎮 Библиотека разработчика игр | Gamedev, Unity, Unreal Engine
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека разработчика игр»

Принцип работы protected в Unity

Пометка поля или метода защищенным означает, что только текущий класс и его наследники классы смогут получить доступ к данному типу данных.

Это может быть полезно, когда вы хотите создать базовые характеристики и функциональность, которые будут использовать другие классы, если они являются производными от этого типа.

Сравнительная таблица модификаторов доступа public, protected, private

Protected работает как своего рода промежуточное звено между public и private, позволяя производным классам изменять и использовать защищенные типы, но скрывая их от других, не связанных скриптов.

Например, вы можете создать базовый тип Human, который содержит поле Health и метод Walk.

Если сделать их защищенными, то классы-наследники, такие как Enemy или NPC, смогут вызывать и использовать их, в то время как внешние классы не смогут:

class Human : MonoBehaviour
{
    protected float health;

    protected void Walk()
    {
        // Walk
    }
}

class NPC : Human
{
    private void Start()
    {
        health = 100;
        Walk();
    }
}

class Enemy : Human
{
    private void Start()
    {
        health = 50;
        Walk();
    }
}

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

Виртуальные и переопределенные методы в Unity

При использовании наследования отношения между полями и методами могут быть немного сложнее, чем было описано ранее.

Это происходит главным образом потому, что классы-наследники могут не только использовать, но и изменять методы классов-родителей.

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

Модификаторы abstract, virtual, override и sealed можно использовать для управления отношениями между иерархическими классами.

Модификатор abstract

abstract — этим модификатором могут быть помечены методы и свойства в абстрактных классах. Абстрактные методы не имеют тела и обязаны быть реализованы в классах-наследниках.

Модификатор virtual

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

Модификатор override

override — этим ключевым словом обозначаются те методы и свойства, которые переопределяют (то есть дополняют или изменяют) что-либо из базового класса.

Модификатор sealed

sealed — это ключевое слово служит для того, чтобы запрещать классам-наследникам переопределять методы или наследоваться от класса, который помечен таким модификатором.

Эти модификаторы используются в дополнение к модификаторам доступа, поэтому вы обычно будете видеть их вместе с ключевыми словами public, private и protected.

Почему бы просто не сделать все общедоступным?

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

В конце концов, если вы единственный человек, который работает над своей игрой, то знаете, как все должно использоваться.

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

В результате, как правило, хорошей практикой является делать что-то публичным только тогда, когда это действительно необходимо, ограничивая функциональность скрипта несколькими основными открытыми методами.

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

***

А какой подход к организации доступа к данным используете вы в своих проектах? Делитесь опытом в комментариях!

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

matyushkin
18 марта 2020

ТОП-10 книг по C#: от новичка до профессионала

Отобрали актуальные книги по C#, .NET, Unity c лучшими оценками. Расположил...
Библиотека программиста
25 августа 2019

Почему C# программисты скоро будут нарасхват

C# программисты становятся более востребованными благодаря развивающейся эк...
Библиотека программиста
23 июня 2017

Разработка игр – это просто: 12 этапов изучения геймдева

Разработка игр на плаву, она перспективна и набирает популярность. Мы подго...