Параметры процедур и функций во встроенном языке 1С: по ссылке, по значению, по умолчанию

· 10 мин

Содержание

Значение передают в функцию 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.

Выражения и вызовы функций туда писать нельзя: Дата = ТекущаяДатаСеанса() или Список = Новый Массив не скомпилируются. Обходной путь — значение Неопределено и проверка в начале метода:

Функция ОстаткиНаДату(Знач Склад, Знач НаДату = Неопределено)
    Если НаДату = Неопределено Тогда
        НаДату = ТекущаяДатаСеанса();
    КонецЕсли;
    // ...
КонецФункции

Необязательные параметры ставьте в конец списка. Тогда вызывающему коду не придётся считать запятые, а при добавлении нового необязательного параметра в конец старые вызовы продолжат работать без правок.

Перед тем как добавить параметр в существующий метод
Выполнено: 0 / 5

Порядок аргументов и замена именованным параметрам

Именованных параметров в языке 1С нет. Конструкции вида Функция(Ставка = 10) в вызове не существует: аргументы сопоставляются строго по позиции. Пропустить параметр можно только запятой, если у него есть значение по умолчанию.

Так работают и встроенные функции. Например, у СтрНайти пять параметров, и чтобы найти второе вхождение, третий и четвёртый пропускают запятыми:

Позиция = СтрНайти(Текст, ";", , , 2);

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

Функция СформироватьОтчет(Знач Параметры)
    Настройки = Новый Структура;
    Настройки.Вставить("Организация", Неопределено);
    Настройки.Вставить("НачалоПериода", НачалоМесяца(ТекущаяДатаСеанса()));
    Настройки.Вставить("ПоСкладам", Ложь);
    ЗаполнитьЗначенияСвойств(Настройки, Параметры);
    // дальше работаем с Настройки
КонецФункции

Параметры = Новый Структура("Организация, ПоСкладам", Организация, Истина);
СформироватьОтчет(Параметры);

Что даёт такой приём:

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

ЗаполнитьЗначенияСвойств переносит в Настройки только совпадающие по имени ключи, остальные сохраняют значения по умолчанию. Если ключ в вызове написан с опечаткой, он просто не перенесётся — это минус подхода, его закрывают проверкой ключей в отладке.

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

Массивы, структуры и таблицы значений как аргументы

Коллекции — объекты, поэтому на них действует правило из раздела про ссылочные типы: функция, которая меняет переданный массив или таблицу, меняет их и у вызывающего кода, Знач от этого не защищает.

Если функция должна поработать с копией, копию делают явно:

КоллекцияКак получить копию
ТаблицаЗначенийТаблица.Скопировать() — можно с отбором и списком колонок
МассивНовый массив и цикл Для Каждого с Добавить
СтруктураНовая структура и цикл по КлючИЗначение с Вставить
Вложенные коллекцииРекурсивное копирование: поверхностная копия делит вложенные объекты с исходником

Если же нужно гарантировать, что функция ничего не поменяет, передают фиксированную коллекцию:

Настройки = Новый ФиксированнаяСтруктура(Параметры);
Обработать(Настройки);  // попытка Вставить или изменить ключ вызовет ошибку

Аналогично есть ФиксированныйМассив и ФиксированноеСоответствие. Ошибка при попытке изменения возникает во время выполнения, а не при компиляции, зато сразу показывает место, где код нарушил договорённость.

Ещё два момента про коллекции:

  • 🔸 Клиент-сервер. На сервер с клиента передаются только сериализуемые значения: примитивы, ссылки, Массив, Структура, Соответствие и их фиксированные варианты. ТаблицаЗначений на клиенте недоступна, поэтому из клиентской процедуры её не передать; с формы на сервер уходит реквизит формы типа ДанныеФормыКоллекция, а в таблицу значений его преобразуют на сервере через РеквизитФормыВЗначение.
  • 🔸 Изменение в цикле. Если функция удаляет элементы переданного массива, идите с конца, иначе индексы сдвинутся:
Процедура УдалитьПустые(Список)
    Всего = Список.Количество();
    Для Сч = 1 По Всего Цикл
        Индекс = Всего - Сч;
        Если Не ЗначениеЗаполнено(Список[Индекс]) Тогда
            Список.Удалить(Индекс);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

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

Ошибки при передаче параметров и как их избежать

Диагностика: выберите симптом

Привычки, которые избавляют от большинства проблем:

  1. Входные параметры объявляйте с Знач, выходные — без, и называйте выходные по смыслу результата.
  2. Не используйте параметр как рабочую переменную, даже с Знач: отдельная локальная переменная читается проще.
  3. Больше пяти параметров — повод перейти на структуру.
  4. В серверные методы передавайте ссылки и простые значения, а не объекты.
  5. Не проверяйте тип там, где можно договориться: если метод принимает массив, не принимайте «массив или одно значение» без явной нужды. Если всё же нужно, проверяйте через ТипЗнч(Параметр) = Тип("Массив").
Можно ли передать в функцию переменное число аргументов?
Нет, число параметров фиксировано объявлением. Переменный набор значений передают массивом или структурой.
Можно ли узнать, передал ли вызывающий код необязательный параметр?
Напрямую нет. Задайте значением по умолчанию Неопределено и проверяйте его в начале метода.
Как вернуть из функции несколько значений?
Верните структуру с нужными ключами. Второй способ — параметры без Знач, в которые функция кладёт результат, но структура нагляднее.
Передаётся ли форма или её реквизиты в серверный метод без контекста?
Нет. В &НаСервереБезКонтекста доступны только переданные параметры, поэтому нужные данные формы передают явно аргументами.
Влияет ли Знач на скорость?
Внутри одного компьютера разница незаметна. При вызове сервера с клиента Знач сокращает трафик, потому что параметры не возвращаются обратно.