При разработке алгоритмов и бизнес-логики на платформе 1С разработчики часто используют составные логические условия с операторами И (AND) и ИЛИ (OR). Однако поведение системы при выполнении таких условий кардинально отличается в зависимости от версии платформы — 1С:Предприятие 7.7 или 1С:Предприятие 8.х. Разберем подробно, почему возникает эта разница, к каким критическим ошибкам она может привести и как правильно составлять безопасный код.
Выясним основной технический нюанс работы платформы с логическими выражениями.
1С:Предприятие 7.7 — Полная схема вычисления (Full Evaluation)
В платформе 7.7 всегда вычисляются все подвыражения, входящие в составное условие, независимо от результата первой части выражения:
Условие1 И Условие2 первая часть Условие1 возвращает Ложь, платформа 1С 7.7 всё равно продолжит выполнение и вычислит Условие2.Условие1 ИЛИ Условие2 первая часть Условие1 возвращает Истина, правая часть Условие2 также будет вычислена.1С:Предприятие 8.х — Сокращенная схема вычисления (Short-circuit Evaluation)
В платформе 8.х используются так называемые «ленивые вычисления». Платформа прекращает вычисление логического выражения сразу же, как только итоговый результат становится гарантирован:
И: если первое условие равно Ложь, второе условие даже не начинает вычисляться.ИЛИ: если первое условие равно Истина, проверка последующих условий пропускается.Проанализируем простую ситуацию с помощью наглядного теста. Напишем следующий код в платформе 1С 7.7:
Если (1 > 2) И (0 / 0 = 5) Тогда
Предупреждение("Условие выполнено");
КонецЕсли;
Очевидно, что левая часть (1 > 2) ложна. Если бы платформа использовала сокращенную схему, правая часть (0 / 0 = 5) не должна была исполняться. Однако при запуске этого кода в 1С 7.7 система выдаст критическую ошибку «Деление на ноль». Это наглядно подтверждает, что в 1С 7.7 всегда проверяются все операнды логической цепочки.
Рассмотрим реальный практический пример, с которым сталкиваются разработчики. Допустим, нам нужно проверить условие по документу:
Если (Конт.Вид() = "РеализацияРозница") И (Найти(Врег(Контрагент.Наименование), "VISA") > 0) Тогда
// Выполнение операций
КонецЕсли;
Если текущий документ не является документами вида «РеализацияРозница», или у него не выбран контрагент (значение Контрагент не заполнено), обращение к Контрагент.Наименование вызовет ошибку времени выполнения (Runtime Error). Из-за того, что в 1С 7.7 вычисляются обе части выражения, программа завершит работу с ошибкой обращения к реквизиту незаполненного объекта, несмотря на то, что первая проверка вернула Ложь.
Разберем основные методы обхода этого ограничения в 1С:Предприятие 7.7.
Самый простой, понятный и надежный способ — разбить единое составное условие на несколько вложенных операторов Если ... Тогда. В этом случае внутренние блоки кода гарантированно не выполнятся, если внешняя проверка возвращает Ложь.
Если Конт.Вид() = "РеализацияРозница" Тогда
Если Контрагент.Выбран() = 1 Тогда
Если Найти(Врег(Контрагент.Наименование), "VISA") > 0 Тогда
// Безопасное выполнение логики
КонецЕсли;
КонецЕсли;
КонецЕсли;
Посмотрим на пример, когда перед выполнением сложного составного условия логические значения или наименования реквизитов предварительно рассчитываются и записываются в промежуточные переменные.
// Проверяем наличие реквизита или заполненность контрагента до основного условия
Если глЕстьРеквизитШапки("Контрагент", Конт.Вид()) = 1 Тогда
ЛокальныйКонтрагент = Конт.Контрагент;
Иначе
ЛокальныйКонтрагент = ПолучитьПустоеЗначение("Справочник.Контрагенты");
КонецЕсли;
Если ЛокальныйКонтрагент.Выбран() = 1 Тогда
НаимКонтрагента = Врег(ЛокальныйКонтрагент.Наименование);
Иначе
НаимКонтрагента = "";
КонецЕсли;
// Теперь сложное составное условие абсолютно безопасно
Если ((Конт.Вид() = "РеализацияРозница") И (Найти(НаимКонтрагента, "VISA") > 0) И (Конт.ДокОснование.Вид() = "ЗаявкаПокупателя"))
ИЛИ (Конт.Вид() = "ОтчетККМ")
ИЛИ ((Конт.Вид() = "ЧекККМ") И (Конт.ДокОснование.Вид() = "ЗаявкаПокупателя")) Тогда
// Выполнение бизнес-логики
КонецЕсли;
В платформе 1С 8.х благодаря «ленивым вычислениям» разработчики могут писать безопасный код в одну строку:
Если ЗначениеЗаполнено(Ссылка) И Ссылка.Реквизит = "Значение" Тогда
// Если Ссылка не заполнена, обращение к Ссылка.Реквизит не произойдет
КонецЕсли;
Для повышения производительности в 1С 8.х рекомендуется соблюдать следующие правила оформления условий:
И в самое начало (слева) нужно ставить самые быстрые проверки или условия, которые с наибольшей вероятностью вернуть Ложь.ИЛИ в самое начало нужно ставить условия, которые наиболее часто возвращают Истина.