Операторы в коде на языке 1С: что делает каждый знак

· 9 мин

Содержание

В языке 1С операторов немного, но у каждого есть нюанс, из-за которого новичок ловит ошибку или получает не тот результат. Равенство здесь пишется одним знаком =, а не двумя. Плюс складывает числа, склеивает строки и сдвигает даты — в зависимости от типов. А логические И и ИЛИ не всегда досчитывают выражение до конца.

Ниже — каждая группа операторов с коротким объяснением и рабочим примером: арифметика, сравнение, логика, работа с типами, коллекции, доступ к данным и специализированные конструкции вроде тернарного ?(). Примеры рассчитаны на платформу 8.3 и встроенный язык в толстом и тонком клиенте.

Арифметические операторы и двойная роль плюса

Базовый набор знаком всем: + сложение, - вычитание, * умножение, / деление. Пятый оператор — %, остаток от деления:

Результат = 10 % 3; // 1
Чётное = (Число % 2 = 0); // признак чётности

Деление целых чисел может вернуть дробь: 7 / 2 даёт 3.5, а не 3. Целую часть берут функцией Цел(), округление — Окр().

Деление на ноль вызывает ошибку выполнения и останавливает процедуру. Перед делением проверяйте делитель: ?(Делитель = 0, 0, Сумма / Делитель).

Главная ловушка — перегрузка оператора +. Он работает по типу левого операнда:

  • число + число → арифметика;
  • строка + что угодно → склейка (конкатенация);
  • дата + число → сдвиг даты на это число секунд.
Итог = "Сумма: " + 500; // "Сумма: 500" — число превратилось в строку
ЧасПозже = ТекущаяДата() + 3600; // +3600 секунд = +1 час

Поэтому при выводе чисел в строку лучше задавать формат явно, иначе получите разряды и лишние нули из системных настроек:

Текст = "Остаток: " + Формат(Остаток, "ЧДЦ=2; ЧРГ=' '");
// либо через шаблон:
Текст = СтрШаблон("Остаток: %1", Остаток);

Вычитание дат ведёт себя симметрично сложению: разница двух дат — это число секунд между ними. Чтобы получить дни, делите на 86400.

Операторы сравнения: один знак равенства

Здесь главное отличие от C-подобных языков: равенство пишется одним знаком =. Двойного == в языке 1С нет вообще.

ОператорЗначение
=равно
<>не равно
< >меньше / больше
<= >=меньше или равно / больше или равно

Один и тот же знак = служит и присваиванием, и сравнением — платформа различает их по контексту. Как оператор (команда) — это присваивание, внутри выражения — сравнение:

Ставка = 20; // присваивание: записали 20 в переменную
Если Ставка = 20 Тогда // сравнение: проверяем равенство
    // ...
КонецЕсли;

Сравнивать можно значения разных типов, но результат не всегда очевиден. Два значения разных типов никогда не равны: строка "5" не равна числу 5. Поэтому перед сравнением приводите операнды к одному типу функцией Число(), Строка() или проверяйте тип заранее.

Неопределено и NULL — разные значения. NULL приходит из запросов к базе (пустое поле соединения), Неопределено — из незаполненных переменных и коллекций. Сравнивать поле запроса с Неопределено бессмысленно — используйте NULL или функцию ЕстьNULL() в тексте запроса.

Логические операторы и ленивое вычисление

Логических операторов три, и пишутся они словами: И (конъюнкция), ИЛИ (дизъюнкция), НЕ (отрицание).

Если Документ.Проведён И НЕ Документ.ПометкаУдаления Тогда
    // проведён и не помечен на удаление
КонецЕсли;

Ключевая особенность — ленивое (сокращённое) вычисление. Платформа считает выражение слева направо и останавливается, как только результат предрешён:

  • в А И Б — если А ложно, Б уже не вычисляется;
  • в А ИЛИ Б — если А истинно, Б не вычисляется.

Это не просто оптимизация, а приём защиты от ошибок. Порядок условий имеет значение:

// Безопасно: сначала проверяем, что значение заполнено
Если ЗначениеЗаполнено(Ссылка) И Ссылка.Проведён Тогда

Если поменять условия местами, обращение к .Проведён у пустой ссылки вызовет ошибку — до неё дошли бы, потому что первая проверка не отсеяла пустое значение. Ставьте «отсекающее» условие первым — это убирает половину ошибок «обращение к несуществующему реквизиту».

Приоритет: НЕ выше И, И выше ИЛИ. Чтобы не держать это в голове, группируйте скобками:

Если (Тип = "Приход" ИЛИ Тип = "Возврат") И Сумма > 0 Тогда

Без скобок И «притянул» бы к себе Сумма > 0 только к Возврат, и логика сломалась бы незаметно.

Работа с типами: ТипЗнч, Тип и оператор Новый

Язык 1С динамически типизирован: тип хранит значение, а не переменная. Три конструкции для работы с типами встречаются в любом модуле.

ТипЗнч(Значение) возвращает тип переданного значения. Тип("ИмяТипа") возвращает тип по его строковому имени. Их сравнивают между собой:

Если ТипЗнч(Значение) = Тип("Строка") Тогда
    // пришла строка
ИначеЕсли ТипЗнч(Значение) = Тип("Число") Тогда
    // пришло число
КонецЕсли;

Для ссылочных типов имя пишется полностью: Тип("СправочникСсылка.Номенклатура"), Тип("ДокументСсылка.РеализацияТоваровУслуг").

Новый создаёт объект. Он нужен для коллекций, работы с файлами, HTTP-запросов, структур данных:

Запрос = Новый Запрос;
Файл = Новый Файл("C:\data\export.txt");
ТЗ = Новый ТаблицаЗначений;

Проверять заполненность примитивов лучше не через ТипЗнч, а функцией ЗначениеЗаполнено() — она сама знает «пустые» значения для каждого типа (нулевая дата, пустая строка, пустая ссылка).

Коллекции: Массив, Структура, Соответствие

Три коллекции закрывают большинство задач. Отличаются они тем, по какому ключу достают значение.

Массив — упорядоченный список, доступ по числовому индексу с нуля:

Строки = Новый Массив;
Строки.Добавить("первая");
Строки.Добавить("вторая");
Сообщить(Строки[0]); // первая
Сообщить(Строки.Количество()); // 2

Структура — набор «ключ-значение», где ключ всегда строка и обязан быть корректным идентификатором (буквы, цифры, без пробелов). Значение достают через точку или скобки:

Параметры = Новый Структура;
Параметры.Вставить("Склад", ВыбранныйСклад);
Параметры.Вставить("Дата", ТекущаяДата());
Сообщить(Параметры.Склад); // через точку
Сообщить(Параметры["Дата"]); // через скобки

Соответствие — тоже «ключ-значение», но ключом может быть любой тип: ссылка, число, дата, другой объект. Это главное отличие от структуры:

ОстаткиПоТоварам = Новый Соответствие;
ОстаткиПоТоварам.Вставить(НоменклатураСсылка, 15);
Остаток = ОстаткиПоТоварам.Получить(НоменклатураСсылка);

Короткая шпаргалка, что выбрать:

ЗадачаКоллекция
Просто перечень значений по порядкуМассив
Фиксированный набор именованных полейСтруктура
Поиск значения по произвольному ключу (ссылке, числу)Соответствие

При обходе коллекции в цикле не удаляйте из неё элементы по возрастающему индексу — после удаления элементы сдвигаются, и часть пропустится. Идите с конца: Для Сч = Массив.ВГраница() По 0 Цикл ... КонецЦикла; — так удаление не ломает нумерацию оставшихся.

Проверьте себя
Чем `Соответствие` отличается от `Структуры`?
Что вернёт выражение `10 % 3`?
Сработает ли обращение `Ссылка.Проведён`, если в условии `ЗначениеЗаполнено(Ссылка) И Ссылка.Проведён` ссылка пустая?

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

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

Точка . — обращение к реквизиту, свойству или методу по известному имени:

Дата = Документ.Дата;
Контрагент = Документ.Контрагент.НаименованиеПолное;
Документ.Записать();

Цепочка точек разыменовывает ссылки: Документ.Контрагент.ОсновнойБанковскийСчёт.Банк пройдёт по трём объектам. Удобно, но каждая точка — это отдельное обращение к базе, поэтому в циклах по тысячам строк длинные цепочки бьют по скорости — такие данные лучше получать запросом.

Квадратные скобки [] — доступ по индексу или ключу, в том числе вычисляемому:

ПерваяСтрока = Таблица[0];
Значение = Соответствие[Ключ]; // ключ в переменной
Поле = Структура["Склад"]; // имя поля строкой

Скобки незаменимы, когда имя поля заранее неизвестно и лежит в переменной. Через точку так не получится — после точки компилятор ждёт конкретный идентификатор, а не выражение.

У табличной части документа оба способа объединяются: Документ.Товары[0].Количество — скобки берут строку по номеру, точка достаёт её колонку.

Условный оператор и выполнение кода строкой

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

Тернарный оператор ?() — короткая замена Если...Тогда...Иначе внутри выражения. Синтаксис: ?(Условие, ЗначениеЕслиИстина, ЗначениеЕслиЛожь):

Скидка = ?(Сумма > 10000, 0.10, 0.05);
Текст = ?(Количество = 1, "товар", "товары");

Удобно для присваивания и формирования строк. Злоупотреблять вложенностью не стоит: три-четыре ?() друг в друге читаются хуже обычного Если.

Вычислить("выражение") — вычисляет строку как выражение и возвращает результат. Выполнить("оператор") — исполняет строку как оператор (или несколько), ничего не возвращая:

Результат = Вычислить("2 + 2 * 2"); // 6
Выполнить("Сообщить('Привет')"); // выполнит команду

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

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

Приоритет операторов и порядок вычисления

Когда в одном выражении смешаны знаки, платформа считает их в фиксированном порядке. От старшего к младшему:

  1. Унарный минус и НЕ.
  2. *, /, %.
  3. +, -.
  4. Сравнения: =, <>, <, >, <=, >=.
  5. И.
  6. ИЛИ.

То есть 2 + 2 * 2 даёт 6, а не 8: умножение раньше сложения. А Сумма > 0 И Остаток > 0 ИЛИ Резерв сначала соберёт обе проверки через И, и только потом применит ИЛИ.

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

Самопроверка перед коммитом кода
Выполнено: 0 / 6

Частые вопросы

Почему `=` не работает как проверка, а присваивает значение?
Один и тот же знак = служит и присваиванием, и сравнением — различается по контексту. Если = стоит отдельной командой (А = 5), это присваивание. Внутри условия Если А = 5 Тогда — сравнение. Двойного == в языке 1С нет.
Чем `Неопределено` отличается от `NULL`?
Неопределено — значение незаполненной переменной или отсутствующего элемента коллекции. NULL приходит из запросов, когда поле соединения пустое. Это разные типы, и напрямую они не равны. В запросах пустоту ловят через ЕстьNULL(), в коде — через ЗначениеЗаполнено().
Как проверить, что в переменной именно строка (или число)?
Сравните результат ТипЗнч(Значение) с нужным типом: ТипЗнч(Значение) = Тип("Строка"). Для проверки «заполнено или нет» удобнее ЗначениеЗаполнено() — он знает пустые значения каждого типа.
Что выбрать — Структуру или Соответствие?
Если ключи — фиксированные имена-идентификаторы (Склад, Дата, Сумма), берите Структуру: к ней можно обращаться через точку. Если ключом служит ссылка, число или другой объект, нужно Соответствие — только оно принимает ключ любого типа.
Почему порядок условий в `И` важен?
Из-за ленивого вычисления: при А И Б, если А ложно, Б не считается. Поставив проверку заполненности первой, вы не дойдёте до обращения к реквизиту пустого значения. Поменяете местами — получите ошибку на пустых данных.