Параметры процедур и функций во встроенном языке 1С: по ссылке, по значению, по умолчанию
Содержание
Значение передают в функцию 1С через параметры в скобках: объявляете Функция Имя(Параметр1, Параметр2) и вызываете Имя(Значение1, Значение2). По умолчанию параметр передаётся по ссылке: если функция присвоит ему новое значение, изменится и переменная в вызывающем коде. Чтобы этого не было, перед именем параметра ставят модификатор Знач.
Типичная история: программист пишет функцию расчёта скидки, внутри «для удобства» переприсваивает параметр Сумма, а потом удивляется, что в вызывающей процедуре сумма документа уже другая. Ниже — как устроены параметры процедур и функций в 1С 8.3, где ссылка, где копия и как не наступить на эти грабли. Правила общие для платформы и не зависят от конфигурации.
Как объявить параметры функции и процедуры
Параметры перечисляют через запятую в скобках после имени. Тип не указывают: встроенный язык 1С динамически типизирован, в параметр можно передать что угодно.
Функция РассчитатьСкидку(Сумма, ПроцентСкидки)
Возврат Окр(Сумма * ПроцентСкидки / 100, 2);
КонецФункции
Процедура ЗаписатьВЖурнал(Текст, Уровень)
// процедура ничего не возвращает
КонецПроцедуры
Чем отличаются два вида методов:
| Что | Функция | Процедура |
|---|---|---|
| Возвращает значение | Да, через Возврат Значение; | Нет, Возврат; только выходит |
| Вызов в выражении | Скидка = РассчитатьСкидку(1000, 5); | Нельзя |
| Вызов отдельной строкой | Можно, результат отбросится | Обычный способ |
Ключевые слова, которые встречаются в объявлении:
- 🔹
Знач— передать параметр по значению (подробно ниже); - 🔹
Экспорт— после закрывающей скобки, делает метод доступным из других модулей; - 🔹
= значение— значение по умолчанию, параметр становится необязательным.
Пример объявления со всем сразу:
Функция ПолучитьОстаток(Знач Номенклатура, Знач Склад = Неопределено) Экспорт
Тип и смысл параметров документируют комментарием перед методом в блоке // Параметры:. Конфигуратор и EDT показывают его во всплывающей подсказке при вызове, а коллегам не приходится гадать, что передавать.
Если при вызове передать меньше аргументов, чем обязательных параметров, модуль не скомпилируется с ошибкой Недостаточно фактических параметров. Лишние аргументы дают Слишком много фактических параметров.
Передача чисел, строк и дат по значению
Числа, строки, даты, Булево, Неопределено и Null — простые (примитивные) типы. Сами по себе они неизменяемы: нельзя «дописать» строку на месте, можно только присвоить переменной новую.
Поэтому для простых типов вопрос «по ссылке или по значению» сводится к одному: меняется ли переменная вызывающего кода, если функция присвоит параметру новое значение.
Процедура Увеличить(Число)
Число = Число + 1;
КонецПроцедуры
Процедура УвеличитьКопию(Знач Число)
Число = Число + 1;
КонецПроцедуры
А = 5;
Увеличить(А); // А = 6
УвеличитьКопию(А); // А по-прежнему 6
Без Знач процедура Увеличить работает с той же переменной А. С Знач она получает свою копию, и присваивание внутри никого не задевает.
Если в параметр передано не имя переменной, а выражение или литерал, менять снаружи нечего:
Увеличить(А + 0); // А не изменится: передан результат выражения
Увеличить(10); // ошибки нет, присваивание уйдёт во временное значение
Возвращать результат через параметр без Знач можно и иногда нужно (например, функция возвращает Булево «успешно», а текст ошибки кладёт в параметр). Но делайте это осознанно и называйте такой параметр понятно: ТекстОшибки, Результат. Случайное переприсваивание входного параметра — частый источник плавающих ошибок.
Объекты и ссылочные типы в параметрах
Здесь начинается путаница. Массив, структура, таблица значений, ДокументОбъект, СправочникОбъект — это объекты. Переменная хранит не сам объект, а указатель на него. При передаче в функцию копируется указатель, а объект остаётся один.
Отсюда правило:
- ✅ изменение содержимого объекта внутри функции (добавить строку, поменять реквизит) видно снаружи всегда — и с
Знач, и без; - ✅ присваивание параметру нового объекта видно снаружи только без
Знач.
Процедура ДополнитьСписок(Знач Список)
Список.Добавить(3); // изменит массив вызывающего кода
Список = Новый Массив; // на вызывающий код не повлияет
Список.Добавить(100); // уйдёт в новый, локальный массив
КонецПроцедуры
Мас = Новый Массив;
Мас.Добавить(1);
Мас.Добавить(2);
ДополнитьСписок(Мас);
// Мас содержит 1, 2, 3
Ссылки на объекты базы (СправочникСсылка.Номенклатура, ДокументСсылка.РеализацияТоваровУслуг) ведут себя как простые значения: ссылка неизменяема, через неё нельзя поменять данные. Чтобы изменить элемент, внутри функции получают объект:
Процедура ПереименоватьЭлемент(Знач Ссылка, Знач НовоеНаименование)
Объект = Ссылка.ПолучитьОбъект();
Объект.Наименование = НовоеНаименование;
Объект.Записать();
КонецПроцедуры
Передавать в серверную функцию лучше ссылку, а не готовый объект: ссылка лёгкая, объект тяжёлый, а с клиента объекты прикладных типов передать вообще нельзя — их там не существует.
Модификатор Знач: когда ставить обязательно
Сводка поведения:
| Что делает функция с параметром | Без Знач | С Знач |
|---|---|---|
Присваивает новое значение (П = 10) | Меняется переменная снаружи | Не меняется |
Меняет содержимое объекта (П.Добавить(1)) | Видно снаружи | Видно снаружи |
| Читает параметр | Разницы нет | Разницы нет |
Ситуации, где Знач ставят почти всегда.
Вызов сервера с клиента
Когда клиентская процедура формы вызывает метод &НаСервере или &НаСервереБезКонтекста, параметры сериализуются и уходят на сервер. Если параметр объявлен без Знач, после выполнения платформа передаёт его значение обратно на клиент — ведь функция могла его изменить. Это лишний трафик, особенно для массивов и структур.
&НаКлиенте
Процедура ЗаполнитьЦены(Команда)
Цены = ПолучитьЦены(Объект.Дата, СписокТоваров());
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьЦены(Знач Дата, Знач Товары)
// запрос к регистру цен
КонецФункции
С Знач данные идут в одну сторону, назад возвращается только результат функции. Там, где клиент и сервер общаются через тонкий канал (веб-клиент, удалённые пользователи), разница заметна.
Параметр меняется внутри как рабочая переменная
Если внутри функции параметр используется как счётчик, остаток или промежуточная сумма, без Знач эти изменения «протекут» наружу. Пример с распределением:
Функция РаспределитьОстаток(Знач Остаток, Строки)
Для Каждого Строка Из Строки Цикл
Строка.Сумма = Мин(Строка.Потребность, Остаток);
Остаток = Остаток - Строка.Сумма;
КонецЦикла;
Возврат Остаток;
КонецФункции
Без Знач переменная, переданная как Остаток, после вызова обнулилась бы в вызывающем коде.
Экспортные методы общих модулей
Метод общего модуля вызывают из разных мест, и автор вызова не обязан помнить, что метод портит его переменные. Знач у входных параметров — дешёвая страховка.
Необязательные параметры и значения по умолчанию
Значение по умолчанию задают знаком = в объявлении. Такой параметр можно не передавать:
Функция СуммаСНДС(Сумма, Ставка = 20, Округлять = Истина)
Результат = Сумма * (100 + Ставка) / 100;
Если Округлять Тогда
Результат = Окр(Результат, 2);
КонецЕсли;
Возврат Результат;
КонецФункции
СуммаСНДС(1000); // 1200
СуммаСНДС(1000, 10); // 1100
СуммаСНДС(1000, , Ложь); // 1200, ставка взята по умолчанию
Значением по умолчанию может быть только константа-литерал:
- 🔹 число, строка, дата в литерале (
'20250101'); - 🔹
Истина,Ложь; - 🔹
Неопределено,Null.
Выражения и вызовы функций туда писать нельзя: Дата = ТекущаяДатаСеанса() или Список = Новый Массив не скомпилируются. Обходной путь — значение Неопределено и проверка в начале метода:
Функция ОстаткиНаДату(Знач Склад, Знач НаДату = Неопределено)
Если НаДату = Неопределено Тогда
НаДату = ТекущаяДатаСеанса();
КонецЕсли;
// ...
КонецФункции
Необязательные параметры ставьте в конец списка. Тогда вызывающему коду не придётся считать запятые, а при добавлении нового необязательного параметра в конец старые вызовы продолжат работать без правок.
Порядок аргументов и замена именованным параметрам
Именованных параметров в языке 1С нет. Конструкции вида Функция(Ставка = 10) в вызове не существует: аргументы сопоставляются строго по позиции. Пропустить параметр можно только запятой, если у него есть значение по умолчанию.
Так работают и встроенные функции. Например, у СтрНайти пять параметров, и чтобы найти второе вхождение, третий и четвёртый пропускают запятыми:
Позиция = СтрНайти(Текст, ";", , , 2);
Когда параметров больше четырёх-пяти, вызов превращается в ряд запятых и ошибиться в позиции легко. Практическое решение — передавать одну структуру параметров:
Функция СформироватьОтчет(Знач Параметры)
Настройки = Новый Структура;
Настройки.Вставить("Организация", Неопределено);
Настройки.Вставить("НачалоПериода", НачалоМесяца(ТекущаяДатаСеанса()));
Настройки.Вставить("ПоСкладам", Ложь);
ЗаполнитьЗначенияСвойств(Настройки, Параметры);
// дальше работаем с Настройки
КонецФункции
Параметры = Новый Структура("Организация, ПоСкладам", Организация, Истина);
СформироватьОтчет(Параметры);
Что даёт такой приём:
- ✅ ключи читаются как имена: видно, какое значение что значит;
- ✅ порядок не важен;
- ✅ новый параметр добавляется без правки всех вызовов;
- ✅ значения по умолчанию могут быть выражениями, а не только литералами.
ЗаполнитьЗначенияСвойств переносит в Настройки только совпадающие по имени ключи, остальные сохраняют значения по умолчанию. Если ключ в вызове написан с опечаткой, он просто не перенесётся — это минус подхода, его закрывают проверкой ключей в отладке.
Тот же принцип используется в асинхронных вызовах: в ОписаниеОповещения дополнительные данные передают структурой в параметре ДополнительныеПараметры, и обработчик получает их последним аргументом.
Массивы, структуры и таблицы значений как аргументы
Коллекции — объекты, поэтому на них действует правило из раздела про ссылочные типы: функция, которая меняет переданный массив или таблицу, меняет их и у вызывающего кода, Знач от этого не защищает.
Если функция должна поработать с копией, копию делают явно:
| Коллекция | Как получить копию |
|---|---|
ТаблицаЗначений | Таблица.Скопировать() — можно с отбором и списком колонок |
Массив | Новый массив и цикл Для Каждого с Добавить |
Структура | Новая структура и цикл по КлючИЗначение с Вставить |
| Вложенные коллекции | Рекурсивное копирование: поверхностная копия делит вложенные объекты с исходником |
Если же нужно гарантировать, что функция ничего не поменяет, передают фиксированную коллекцию:
Настройки = Новый ФиксированнаяСтруктура(Параметры);
Обработать(Настройки); // попытка Вставить или изменить ключ вызовет ошибку
Аналогично есть ФиксированныйМассив и ФиксированноеСоответствие. Ошибка при попытке изменения возникает во время выполнения, а не при компиляции, зато сразу показывает место, где код нарушил договорённость.
Ещё два момента про коллекции:
- 🔸 Клиент-сервер. На сервер с клиента передаются только сериализуемые значения: примитивы, ссылки,
Массив,Структура,Соответствиеи их фиксированные варианты.ТаблицаЗначенийна клиенте недоступна, поэтому из клиентской процедуры её не передать; с формы на сервер уходит реквизит формы типаДанныеФормыКоллекция, а в таблицу значений его преобразуют на сервере черезРеквизитФормыВЗначение. - 🔸 Изменение в цикле. Если функция удаляет элементы переданного массива, идите с конца, иначе индексы сдвинутся:
Процедура УдалитьПустые(Список)
Всего = Список.Количество();
Для Сч = 1 По Всего Цикл
Индекс = Всего - Сч;
Если Не ЗначениеЗаполнено(Список[Индекс]) Тогда
Список.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Индекс считается от исходного размера Всего: проход идёт от последнего элемента к первому, а удаление элемента сдвигает только те, что стоят правее и уже проверены.
Ошибки при передаче параметров и как их избежать
Привычки, которые избавляют от большинства проблем:
- Входные параметры объявляйте с
Знач, выходные — без, и называйте выходные по смыслу результата. - Не используйте параметр как рабочую переменную, даже с
Знач: отдельная локальная переменная читается проще. - Больше пяти параметров — повод перейти на структуру.
- В серверные методы передавайте ссылки и простые значения, а не объекты.
- Не проверяйте тип там, где можно договориться: если метод принимает массив, не принимайте «массив или одно значение» без явной нужды. Если всё же нужно, проверяйте через
ТипЗнч(Параметр) = Тип("Массив").
Можно ли передать в функцию переменное число аргументов?
Можно ли узнать, передал ли вызывающий код необязательный параметр?
Неопределено и проверяйте его в начале метода.Как вернуть из функции несколько значений?
Знач, в которые функция кладёт результат, но структура нагляднее.Передаётся ли форма или её реквизиты в серверный метод без контекста?
&НаСервереБезКонтекста доступны только переданные параметры, поэтому нужные данные формы передают явно аргументами.Влияет ли Знач на скорость?
Знач сокращает трафик, потому что параметры не возвращаются обратно.