Необязательные аргументы

Разрабатывая функцию, мы выводим абстракцию. То, как работать с абстракцией определяет её интерфейс,  который представляется сигнатурой нашей функцией-метода. Сигнатура – это тип возвращаемого значения и список параметров.

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

Фрагмент 1.33

static void Main(string[] args)
{
    Console.WriteLine("Warning!", ConsoleColor.Yellow);
    Console.WriteLine("Error!", ConsoleColor.Red);
}

static void WriteColoredMessage(string message, ConsoleColor color)
{
    ConsoleColor oldColor = Console.ForegroundColor;

    Console.ForegroundColor = color;
    Console.WriteLine(message);

    Console.ForegroundColor = oldColor;
}

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

Фрагмент 1.34

static void Main(string[] args)
{
    Console.WriteLine("Warning!", ConsoleColor.Yellow);
    Console.WriteLine("Error!", ConsoleColor.Red);
    Console.WriteLine("Empty message!");
}

static void WriteColoredMessage(string message, ConsoleColor color = ConsoleColor.White)
{
    ConsoleColor oldColor = Console.ForegroundColor;

    Console.ForegroundColor = color;
    Console.WriteLine(message);

    Console.ForegroundColor = oldColor;
}

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

Рассмотрим пример из Unity. Там есть метод Raycast, который кидает луч в физическом пространстве и сообщает нам о столкновении с ним. Сигнатура одной из перегрузок такая.

public static bool Raycast(Vector3 origin, Vector3 direction, float maxDistance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal);

Мы можем вызывать методы вот так. Рейкаст полетит с нулевых координат направо на расстоянии в 100 юнитов.

Physics.Raycast(Vector3.zero, Vector3.right, 100);

А можем, например, так, и он будет лететь с нуля направо в бесконечность.

Physics.Raycast(Vector3.zero, Vector3.right);

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

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

Physics.Raycast(Vector3.zero, Vector3.right, layerMask: _mask);

Здесь я в аргумент layerMask передал значение гипотетического поля _mask, при этом пропустив distance.

Эволюция API

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

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

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

В случае со значимыми типами, которые, раньше в принципе не имели определения в логике метода, мы можем воспользоваться nullable типами. Это может позволить нам определить: в метод всё-таки что-то передали или нам стоит работать будто ничего не происходило?

Если вы нашли ошибку, пожалуйста выделите её и нажмите Ctrl+Enter.


Leave a Reply

Your email address will not be published. Required fields are marked *