Процедуры и функции
Процедуры и функции — способ дать имя куску кода, чтобы вызывать его многократно и не копировать одно и то же по всему модулю.
Процедура выполняет действие и ничего не возвращает; функция выполняет действие и возвращает результат через
Возврат. Разница ровно в этом: нужен ли вам ответ обратно.
Как только код перерастает десяток строк, его пора разбивать на подпрограммы. В 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, Б) — ошибка: после параметра со значением по умолчанию не может идти обычный. Сначала обязательные, потом — со значениями по умолчанию.
Итоги
Процедура ... КонецПроцедуры— действие без результата;Функция ... КонецФункциисВозврат— с результатом.Возвратнемедленно завершает функцию; годится для ранних выходов.- Параметры по умолчанию передаются по ссылке;
Значделает копию и защищает оригинал. - Локальные переменные видны только внутри метода; модульные (
Перемвне процедур) — всему модулю. Экспортоткрывает метод для вызова из других модулей; без него он «приватный».