Операторы в коде на языке 1С: что делает каждый знак
Содержание
В языке 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 Цикл ... КонецЦикла; — так удаление не ломает нумерацию оставшихся.
Доступ к данным: точка и квадратные скобки
Два оператора достают данные из объектов. Выбор между ними — не вопрос вкуса, у каждого своя область.
Точка . — обращение к реквизиту, свойству или методу по известному имени:
Дата = Документ.Дата;
Контрагент = Документ.Контрагент.НаименованиеПолное;
Документ.Записать();
Цепочка точек разыменовывает ссылки: Документ.Контрагент.ОсновнойБанковскийСчёт.Банк пройдёт по трём объектам. Удобно, но каждая точка — это отдельное обращение к базе, поэтому в циклах по тысячам строк длинные цепочки бьют по скорости — такие данные лучше получать запросом.
Квадратные скобки [] — доступ по индексу или ключу, в том числе вычисляемому:
ПерваяСтрока = Таблица[0];
Значение = Соответствие[Ключ]; // ключ в переменной
Поле = Структура["Склад"]; // имя поля строкой
Скобки незаменимы, когда имя поля заранее неизвестно и лежит в переменной. Через точку так не получится — после точки компилятор ждёт конкретный идентификатор, а не выражение.
У табличной части документа оба способа объединяются: Документ.Товары[0].Количество — скобки берут строку по номеру, точка достаёт её колонку.
Условный оператор и выполнение кода строкой
Отдельно стоит группа операторов, которые делают язык гибким.
Тернарный оператор ?() — короткая замена Если...Тогда...Иначе внутри выражения. Синтаксис: ?(Условие, ЗначениеЕслиИстина, ЗначениеЕслиЛожь):
Скидка = ?(Сумма > 10000, 0.10, 0.05);
Текст = ?(Количество = 1, "товар", "товары");
Удобно для присваивания и формирования строк. Злоупотреблять вложенностью не стоит: три-четыре ?() друг в друге читаются хуже обычного Если.
Вычислить("выражение") — вычисляет строку как выражение и возвращает результат. Выполнить("оператор") — исполняет строку как оператор (или несколько), ничего не возвращая:
Результат = Вычислить("2 + 2 * 2"); // 6
Выполнить("Сообщить('Привет')"); // выполнит команду
Эти операторы применяют для гибких настроек: формулы в пользовательских полях, динамическая отработка условий. Но у них есть цена.
Выполнить и Вычислить работают с тем, что видно в текущем контексте, медленнее обычного кода и опасны, если в строку попадают данные извне — это открывает инъекцию чужого кода. На тонком клиенте есть ограничения: часть серверного контекста недоступна. Используйте их точечно и никогда не передавайте туда непроверенный пользовательский ввод.
Приоритет операторов и порядок вычисления
Когда в одном выражении смешаны знаки, платформа считает их в фиксированном порядке. От старшего к младшему:
- Унарный минус и
НЕ. *,/,%.+,-.- Сравнения:
=,<>,<,>,<=,>=. И.ИЛИ.
То есть 2 + 2 * 2 даёт 6, а не 8: умножение раньше сложения. А Сумма > 0 И Остаток > 0 ИЛИ Резерв сначала соберёт обе проверки через И, и только потом применит ИЛИ.
Запоминать всю таблицу необязательно — проще ставить скобки там, где логика неочевидна. Скобки читаются быстрее, чем мысленный разбор приоритетов, и защищают от тихих ошибок, которые не всплывают на простых данных и ломаются на боевых.
Частые вопросы
Почему `=` не работает как проверка, а присваивает значение?
= служит и присваиванием, и сравнением — различается по контексту. Если = стоит отдельной командой (А = 5), это присваивание. Внутри условия Если А = 5 Тогда — сравнение. Двойного == в языке 1С нет.Чем `Неопределено` отличается от `NULL`?
Неопределено — значение незаполненной переменной или отсутствующего элемента коллекции. NULL приходит из запросов, когда поле соединения пустое. Это разные типы, и напрямую они не равны. В запросах пустоту ловят через ЕстьNULL(), в коде — через ЗначениеЗаполнено().Как проверить, что в переменной именно строка (или число)?
ТипЗнч(Значение) с нужным типом: ТипЗнч(Значение) = Тип("Строка"). Для проверки «заполнено или нет» удобнее ЗначениеЗаполнено() — он знает пустые значения каждого типа.Что выбрать — Структуру или Соответствие?
Почему порядок условий в `И` важен?
А И Б, если А ложно, Б не считается. Поставив проверку заполненности первой, вы не дойдёте до обращения к реквизиту пустого значения. Поменяете местами — получите ошибку на пустых данных.