Методы

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

Общая информация

При разработке программы часто возникает необходимость повторного использования какого-либо ее фрагмента. Если нам нужно просто повторить этот фрагмент несколько раз, можно использовать цикл. А если этот фрагмент вызывается из разных мест программы, необходимо каким-то образом выделить программный код и оформить его специальным образом. Такой обособленный фрагмент кода называется метод. Метод всегда возвращает какое-либо значение. Подробнее о возвращаемом значении метода будет сказано далее.

Используемые определения

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

Определение метода
Место в исходном коде, где создан метод
Объявление метода
Часть определения метода, которая содержит ключевое слово метод, имя метода, описание формальных параметров в круглых скобках и комментарий, описывающий назначение метода
Тело метода
Инструкции, расположенные между объявлением метода и символом ;, описывающим завершение метода
Вызов метода
Место в тексте программы, где указывается имя метода с указанием фактических параметров и использованием результата работы (только в случае возвращаемого значения)
Определение параметров и передача аргументов
Параметры — это имена переменных, через которые тело метода получает доступ к значениям, которые предоставляются методу во время его вызова. Аргументы — это значения, которые указываются во время вызова метода. Предоставление аргументов методу при его вызове называется передача параметров.
Возврат значения
Передача вызывающему программному коду какого-либо значения при завершении работы метода. Метод не обязательно должен возвращать какое-либо значение

Определение метода

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

метод имя-метода([параметр[, параметр[, ...]]])[: описание-типа]
    тело-метода 
   [возврат[ значение]]
;

Каждый параметр описывается следующим образом:

имя-параметра: тип-параметра[ = значение-по-умолчанию]

имя-параметра задает имя параметра в соответствии со стандартными правилами формирования имен.

тип-параметра указывает, соответственно, тип параметра (в том числе и составной).

значение-по-умолчанию является необязательным элементом описания параметра и позволяет указать значение по умолчанию, т.е. значение, которое будет присвоено параметру, если при вызове метода соответствующий аргумент пропущен. Не допускается пропускать аргумент, если для соответствующего параметра не указано значение по умолчанию. В качестве значения по умолчанию выступает любое выражение, которое может быть вычислено на этапе компиляции:
  • Литералы (включая литералы коллекций) являются вычислимыми.
  • Обращения к константам модулей являются вычислимыми.
  • Операции над вычислимыми являются вычислимыми, кроме следующих исключений:
    • Вызовы методов и обращения к свойствам.
    • Вызовы конструкторов.
    • Операции сложения значения типа Строка со значением другого типа.

Особенности передачи параметров

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

метод Скрипт()
    пер ДляТеста = 5
    Тестовый(ДляТеста)
    пер ПослеВызова = "ДляТеста = " + ДляТеста
;

метод Тестовый(Параметр1: Число)
    Параметр1 = 6
;

В данном примере значение переменной ДляТеста в методе Скрипт() будет равно 5 и до вызова метода Тестовый(), и после вызова этого метода. Такое поведение обеспечивает передача параметра "по значению".

Однако, если в метод передается экземпляр, который располагает методами для изменения своего содержимого, то ситуация будет немного другой. Рассмотрим пример:

метод Скрипт()
    пер ДляТеста = [1, 2]
    Тестовый(ДляТеста)
    пер ПослеВызова = "ДляТеста = " + ДляТеста
;

метод Тестовый(Параметр1: Массив<Число>)
    Параметр1.Очистить()
;

В этом примере до выполнения метода Тестовый() в массиве ДляТеста будет 2 значения: 1 и 2. После вызова метода Тестовый() в массиве не будет ни одного элемента. В то же время, если в методе Тестовый() мы попробуем присвоить параметру Параметр1 какое-либо значение, это не приведет к каким-либо изменениям аргумента (переменной ДляТеста).

Вызов метода с именованными параметрами

При вызове метода допускается передача не только значений параметров в соответствии с их позицией в сигнатуре, то есть позиционных параметров, но и передача параметров вместе с их именами — именованных параметров. Например:

Перемножить(а = 2, б = 3)

Синтаксис:

ИмяМетода(ИмяПараметра1 = Значение, ИмяПараметра2 = Значение, ... , ИмяПараметраN = Значение).

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

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

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

  • Передавать аргументы не обязательно в строгом порядке. Пользователю не требуется искать сигнатуру метода, чтобы вспомнить строгий порядок аргументов. Кроме того, если во время написания кода разработчик поменяет местами параметры в сигнатуре, в том числе параметры одного типа, это не приведет к ошибке во время компиляции или исполнения кода. Сравните:
    метод Разделить(а: Число, б: Число): Число
        возврат а / б
    ;
    
    // Вызовы ниже равносильны:
    Разделить(3, 2)
    Разделить(а = 3, б = 2)
    Разделить(б = 2, а = 3)

    Или:

    метод РассчитатьСебестоимость(Цена: Число, Наценка: Число, Налог: Число): Число
        возврат Цена - (Цена * Наценка / 100 + Цена * Налог / 100)
    ;
    
    // Позиционные параметры
    РассчитатьСебестоимость(1, 10, 100) // Результат: -0.1. Неправильно
    // Именованные параметры
    РассчитатьСебестоимость(Налог = 1, Наценка = 10, Цена = 100) // Результат: 89. Правильно
  • Имена параметров отображаются сразу же при вызове метода. Опять же, пользователю не требуется искать сигнатуру метода, чтобы вспомнить, какой параметр что означает. См. пример выше или:
    // Сигнатура метода
    метод ОтправитьСообщение(Сообщение: Строка, УдалитьПробелы: Булево, ОтправитьБезУведомления: Булево)
    
    // Позиционные параметры
    ОтправитьСообщение(" Привет, мир! ", Истина, Ложь) // Непонятно
    // Именованные параметры
    ОтправитьСообщение(" Привет, мир! ", УдалитьПробелы = Истина, ОтправитьБезУведомления = Ложь) // Понятно
  • Можно выборочно указывать только некоторые, наиболее важные для пользователя параметры. Это может быть особенно удобным, когда в методе используется много необязательных, "служебных" параметров. Для всех этих параметров просто будут использоваться значения по умолчанию, которые указываются при определении метода. Кроме того, для некоторых параметров уже могут быть указаны значения по умолчанию. Так, если для метода РассчитатьСебестоимость сделать значения по умолчанию: для наценки — 10 (%), а для налога — 1 (%), то можно передавать только значение цены. Сравните:
    метод РассчитатьСебестоимость(Налог: Число = 1, Наценка: Число = 10, Цена: Число): Число
        возврат Цена - (Цена * Наценка / 100 + Цена * Налог / 100)
    ;
    
    // Позиционные параметры
    РассчитатьСебестоимость(100) // Ошибка: Параметр "Цена" не имеет значения по умолчанию
    // Именованные параметры
    РассчитатьСебестоимость(Цена = 100) // Результат: 89. Правильно

Смешанная параметризация

При вызове метода можно смешивать позиционную и именованную формы параметризации. Например:

РассчитатьСебестоимость(Налог = 1, Наценка = 10, Цена = 100)
РассчитатьСебестоимость(1, 10, Цена = 100)
При смешанной параметризации действуют следующие правила:
  • Если параметр со значением по умолчанию находится перед параметром с обязательным значением, то в вызове при опускании первого параметра требуется указать второй параметр в именованном виде.
  • Параметры, имеющие значения по умолчанию, находящиеся в конце сигнатуры, можно опускать при вызовах.

Разрешение неоднозначности

При разрешении неоднозначности действуют следующие правила:

  • Если под вызов в именованной форме подходит несколько методов, выдается ошибка неоднозначности.
  • При объявлении методов проверка на возможную неоднозначность вызовов в именованной форме не производится, в отличие от позиционной.
  • Некоторые перегрузки метода не допускают использования именованной формы:
    метод ОтправитьСообщение(Сообщение: Строка, ДлинаСтроки: Число)
    ;
    
    метод ОтправитьСообщение(ДлинаСтроки: Число, Сообщение: Строка)
    ;
    
    ОтправитьСообщение(ДлинаСтроки = 255, Сообщение = " Привет, мир! ") // Ошибка неоднозначности
  • При этом из нескольких перегрузок, которые соответствуют именованному вызову метода, будет выбрана перегрузка, у которой меньше всего аргументов:
    метод ОтправитьСообщение(ДлинаСтроки: Число, Сообщение = "Привет!", УдалитьПробелы = Истина) // Первая перегрузка
    ;
    
    метод ОтправитьСообщение(ДлинаСтроки: Число, УдалитьПробелы: Булево) // Вторая перегрузка
    ;
    
    // Какая перегрузка будет выбрана?
    ОтправитьСообщение(255) // Первая — у второй параметр УдалитьПробелы обязательный
    ОтправитьСообщение(255, Истина) // Вторая — используется позиционная форма вызова
    ОтправитьСообщение(255, " Привет, мир! ") // Первая — в сигнатуре второй перегрузки второй параметр имеет тип Булево, а не Строка
    ОтправитьСообщение(255, " Привет, мир! ", Истина) // Первая — только она имеет все три подходящих параметра
    ОтправитьСообщение(ДлинаСтроки = 255, УдалитьПробелы = Ложь) // Вторая — она короче, ошибки неоднозначности нет

Завершение метода и возвращаемое значение

Метод завершает свою работу после того, как завершается исполнение тела метода. Другим способом прервать исполнение метода является указание в необходимом месте метода ключевого слова возврат.

Если метод должен вернуть какое-либо значение, необходимо это значение указать в качестве параметра ключевого слова возврат. При этом тип фактически возвращаемого значения должен совпадать с типом возвращаемого значения, который указан при описании метода. Тип возвращаемого значения указывается после окончания списка формальных параметров метода. Тип указывается после символа ":" (двоеточие). Однако если метод, в котором нет ключевого слова возврат (или, другими словами, в котором не предполагается возвращаемого значения), будет использован в каком-то выражении или в качестве правого значения инструкции присваивания, то в качестве возвращаемого значения такого метода будет выступать значение Неопределено.

Для методов, смысл которых заключается в возврате значений, можно использовать аннотацию @ПроверятьИспользованиеЗначения. Если метод помечен этой аннотацией и возвращаемое им значение не используется, компилятор выдает ошибку. Пример:
@ПроверятьИспользованиеЗначения
метод Фильтровать(Студенты: Массив<Студент>, Оценка: Число): Массив<Строка>
    пер СписокСтудентов: Массив<Строка>
    для Студент из Студенты
        если Студент.Оценка == Оценка
            СписокСтудентов.Добавить(Студент.Фамилия)
        ;
    ;
    возврат СписокСтудентов
;

Повторное определение (перегрузка) методов

При разработке может возникнуть необходимость создать метод, который отличается количеством или типом параметров. При этом выполняемое действие существенно зависит от типа или количества параметров. Такое поведение можно реализовать двумя способами: составным типом параметров и значениями параметров по умолчанию или повторным определением метода. Повторное определение метода заключается в том, что вы можете описать несколько методов с одним именем. Метод, который имеет несколько объявлений с разным составом параметров, будет называться перегруженным. При этом нужно помнить, что любой вызов перегруженного метода в исходном коде должен позволять компилятору однозначно определить, какой вариант метода необходимо использовать в данном случае. Если определить используемый вариант метода во время компиляции не представляется возможным, произойдет ошибка компиляции. Перегруженные методы не могут различаться только типом возвращаемого значения.

Рассмотрим примеры корректного и некорректного определения перегруженных методов:

1. метод Пример1(Параметр1: Строка|Число)
2. метод Пример1(Параметр1: Строка)
3. метод Пример1(Параметр1: Строка|Булево)

4. метод Пример2(Параметр1: Объект)
5. метод Пример2(Параметр1: Число)
6. метод Пример2(Параметр1: Число, Параметр2: неизвестно)

7. метод Пример3(Параметр1: Строка)
8. метод Пример3(Параметр1: Число)
9. метод Пример3(Параметр1: Строка, Параметр2: неизвестно = 22)

10. метод Пример4(Параметр1: Строка): Строка
11. метод Пример4(Параметр1: Число): Число
12. метод Пример4(Параметр1: Строка): Число

Метод Пример1() (строки 1, 2 и 3) нельзя переопределить предложенным способом. Текущее определение типов параметра метода не позволяет однозначно определить, какой вариант метода необходимо вызвать. Причина в том, что Параметр1 описан с использованием составного типа, при этом в каждом варианте метода присутствует тип Строка.

Метод Пример2() (строки 4, 5 и 6) можно описать, только исключив строку 5. Причиной является тот факт, что тип Объект является базовым для всех типов «1С:Исполнителя». В то же время объявление метода в строке 6 имеет другое количество параметров.

Метод Пример3() (строки 7, 8, 9) можно описать, только исключив строку 9. Причиной является то, что второй параметр имеет значение по умолчанию, а следовательно, если указан один параметр (первый), компилятор не будет понимать, какой из вариантов метода использовать.

Наконец, метод Пример4() (строки 10, 11, 12) может быть описан или строками 10 и 11 или строками 11 и 12. Причиной является тот факт, что перегруженные методы не могут отличаться только типом возвращаемого значения. Поэтому вариант со строками 10 и 12 отпадает.

Также стоит отметить несколько особенностей, касающихся перегруженных методов:
  • Основное назначение перегрузки метода — это удобство разработчика.
  • Если описываемый алгоритм изначально ориентирован на то, что в качестве параметров передаются составные типы, не нужно выполнять перегрузку такого метода.
  • Если алгоритм, описываемый методом, отличается от типа переданного параметра, то стоит рассмотреть возможность перегрузки метода, реализующего такой алгоритм.
  • Перегруженными могут быть не только методы, но и конструкторы.