Процедуры и функции

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

Процедура выполняет действие и ничего не возвращает; функция выполняет действие и возвращает результат через Возврат. Разница ровно в этом: нужен ли вам ответ обратно.

Как только код перерастает десяток строк, его пора разбивать на подпрограммы. В 1С их две разновидности, и путаница между ними — типичная ошибка новичка. Разберёмся, когда что применять и как параметры ведут себя «под капотом».

Процедура: делает, но не возвращает

Процедура объявляется парой Процедура ... КонецПроцедуры. В скобках — параметры (могут отсутствовать). Внутри она что-то делает: пишет в лог, изменяет данные, выводит сообщение.

Процедура ПоказатьПриветствие(Имя)
    Сообщить("Здравствуйте, " + Имя + "!");
КонецПроцедуры

// Вызов процедуры
ПоказатьПриветствие("Мария");
ПоказатьПриветствие("Пётр");

Результат:

Здравствуйте, Мария!
Здравствуйте, Пётр!

Вызов процедуры — это отдельный оператор: имя и аргументы. Использовать её в выражении (х = ПоказатьПриветствие(...)) нельзя — она ничего не отдаёт.

Функция: делает и возвращает

Функция объявляется через Функция ... КонецФункции и обязана вернуть значение оператором Возврат. Этот оператор немедленно завершает функцию и отдаёт результат в точку вызова.

Функция СуммаСНДС(СуммаБезНДС, СтавкаПроцент = 20)
    Налог = СуммаБезНДС * СтавкаПроцент / 100;
    Возврат СуммаБезНДС + Налог;
КонецФункции

Итого = СуммаСНДС(1000);
Сообщить("С НДС 20%: " + Итого);

Итого10 = СуммаСНДС(1000, 10);
Сообщить("С НДС 10%: " + Итого10);

Результат:

С НДС 20%: 1200
С НДС 10%: 1100

Здесь же виден полезный приём — значение параметра по умолчанию: СтавкаПроцент = 20. Если при вызове ставку не передали, подставится 20. Параметры со значением по умолчанию всегда идут в конце списка.

Возврат может прервать функцию раньше

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

Функция БезопасноеДеление(Делимое, Делитель)
    Если Делитель = 0 Тогда
        Возврат 0; // сразу выходим, дальше не считаем
    КонецЕсли;
    Возврат Делимое / Делитель;
КонецФункции

Сообщить(БезопасноеДеление(10, 2)); // 5
Сообщить(БезопасноеДеление(10, 0)); // 0, деления на ноль не будет

Передача параметров: по значению и по ссылке

Это самая коварная тема. По умолчанию в 1С параметры передаются по ссылке: если внутри процедуры изменить параметр, изменение увидит и вызывающий код. Чтобы защитить исходную переменную, параметр помечают ключевым словом Знач (значение) — тогда внутрь попадает копия.

// Параметр по ссылке (по умолчанию) — процедура меняет исходную переменную
Процедура УдвоитьПоСсылке(Число)
    Число = Число * 2;
КонецПроцедуры

// Параметр по значению — работаем с копией, оригинал цел
Процедура УдвоитьПоЗначению(Знач Число)
    Число = Число * 2;
КонецПроцедуры

А = 5;
УдвоитьПоСсылке(А);
Сообщить("После по ссылке: " + А); // 10 — переменная изменилась

Б = 5;
УдвоитьПоЗначению(Б);
Сообщить("После по значению: " + Б); // 5 — оригинал не тронут

Результат:

После по ссылке: 10
После по значению: 5

Практическое правило: если процедура должна вернуть результат через параметр (например, «заполнить переданную структуру») — оставляйте по ссылке. Во всех остальных случаях добавляйте Знач, чтобы случайно не испортить чужие данные. Это делает функцию предсказуемой.

Область видимости переменных

Переменная, объявленная внутри процедуры или функции, — локальная: она живёт только во время её выполнения и снаружи не видна. Это хорошо — подпрограммы не мешают друг другу.

Процедура Посчитать()
    Локальная = 100; // видна только внутри Посчитать
    Сообщить(Локальная);
КонецПроцедуры

Посчитать();
// Здесь обращение к Локальной вызовет ошибку — её тут не существует

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

Экспортные методы

По умолчанию процедура или функция видна только внутри своего модуля. Чтобы её можно было вызвать из других модулей, к объявлению добавляют ключевое слово Экспорт. Так строят «библиотеки» общих методов — например, в общих модулях конфигурации.

// В общем модуле, например "РаботаСДатами"
Функция НачалоМесяца(Дата) Экспорт
    Возврат Дата - День(Дата) * 86400 + 86400 - ТекущаяДата();
КонецФункции

// Вызов из другого модуля через имя общего модуля:
// Итог = РаботаСДатами.НачалоМесяца(ТекущаяДата());

Без Экспорт метод — «приватный», внутренняя кухня модуля. С Экспорт — часть его публичного интерфейса. Экспортируйте только то, что действительно нужно снаружи: это держит модуль в порядке.

Как это работает

При вызове платформа кладёт в стек новый кадр: адрес возврата и локальные переменные-параметры. Для параметра по ссылке в кадр записывается ссылка на исходное значение вызывающего — потому изменения «протекают» наружу. Для параметра Знач создаётся копия значения, изолированная от оригинала. Оператор Возврат кладёт результат в стек и передаёт управление обратно по сохранённому адресу; локальные переменные кадра при этом уничтожаются — вот почему они не видны снаружи. Функция без Возврат вернёт значение Неопределено.

Частые ошибки

Путают процедуру и функцию. Пытаются присвоить результат процедуры (х = МояПроцедура()) — ошибка, процедура ничего не возвращает. И наоборот: забывают Возврат в функции, получая Неопределено вместо ответа.

Случайно портят параметр по ссылке. Внутри процедуры переиспользуют имя параметра как рабочую переменную, не пометив его Знач. У вызывающего «внезапно» меняется переменная. Правило: если не собираетесь возвращать через параметр — ставьте Знач.

Забывают Экспорт. Написали общую функцию, вызывают из другого модуля — «процедура не обнаружена». Причина — нет Экспорт; без него метод невидим снаружи.

Параметр по умолчанию не в конце. Функция Ф(А = 5, Б) — ошибка: после параметра со значением по умолчанию не может идти обычный. Сначала обязательные, потом — со значениями по умолчанию.

Итоги

  • Процедура ... КонецПроцедуры — действие без результата; Функция ... КонецФункции с Возврат — с результатом.
  • Возврат немедленно завершает функцию; годится для ранних выходов.
  • Параметры по умолчанию передаются по ссылке; Знач делает копию и защищает оригинал.
  • Локальные переменные видны только внутри метода; модульные (Перем вне процедур) — всему модулю.
  • Экспорт открывает метод для вызова из других модулей; без него он «приватный».
Проверьте себя
1. Что произойдёт, если внутри процедуры изменить параметр, объявленный БЕЗ ключевого слова Знач?
AНичего, работаем с копией — оригинал у вызывающего не изменится
BИзменится и переменная у вызывающего кода (передача по ссылке)
CВозникнет ошибка компиляции
DЗначение станет Неопределено
2. Зачем к объявлению функции добавляют ключевое слово Экспорт?
AЧтобы функция возвращала значение
BЧтобы её можно было вызвать из других модулей
CЧтобы параметры передавались по значению
DЧтобы функция выполнялась на сервере