Когда мы, как разработчики 1С, сталкиваемся с необходимостью проверки нескольких условий для выполнения определенного действия, перед нами часто встает вопрос: какой синтаксис выбрать? Использовать ли единое условие с логическим оператором И, или же предпочесть вложенные операторы Если? Разберем эту ситуацию подробнее, чтобы понять, в чем заключаются различия, какие подходы предпочтительнее в разных случаях, и есть ли между ними фундаментальная разница.
Давайте начнем с одного из ключевых понятий, которое существенно влияет на выполнение логических выражений в 1С – это укороченная логика, или "short-circuit evaluation".
В платформе 1С 8.x (часто называемой "снеговиком") условия в логических выражениях вычисляются слева направо. Этот процесс останавливается сразу, как только результат всего выражения становится определенным.
И: Если первое условие (операнд) оказывается Ложь, то второе и все последующие условия не будут вычисляться. Ведь для того, чтобы выражение с И было Истина, все его части должны быть Истина. Если одна часть уже Ложь, то весь результат выражения однозначно Ложь, и дальнейшие проверки бессмысленны.ИЛИ: Если первое условие (операнд) оказывается Истина, то второе и все последующие условия не будут вычисляться. Для того, чтобы выражение с ИЛИ было Истина, достаточно, чтобы хотя бы одна его часть была Истина. Если первая часть уже Истина, то весь результат выражения однозначно Истина, и дальнейшие проверки также бессмысленны.Рассмотрим пример из практики, который наглядно демонстрирует полезность укороченной логики. Представим, что мы пишем условие, которое может привести к ошибке выполнения, если не будет применена укороченная логика:
Если (1 <> 1) И (1 / 0 = 0) Тогда
Сообщить("Это сообщение никогда не появится");
КонецЕсли;
В этом примере, первое условие (1 <> 1) является Ложь. Благодаря укороченной логике, второе условие (1 / 0 = 0), которое могло бы вызвать ошибку деления на ноль, не будет вычисляться. В итоге, программа не выдаст ошибку, и блок кода внутри Если не будет выполнен.
Теперь проанализируем, как укороченная логика относится к нашим двум сравниваемым конструкциям:
// Вариант 1: С логическим оператором И
Если Документ.Организация = "тест" И Документ.Подразделение = "Тестовое подразделение" Тогда
// Действие выполняется, если оба условия истинны
КонецЕсли;
// Вариант 2: С вложенными операторами Если
Если Документ.Организация = "тест" Тогда
Если Документ.Подразделение = "Тестовое подразделение" Тогда
// Действие выполняется, если оба условия истинны
КонецЕсли;
КонецЕсли;
С точки зрения чистого выполнения условий и принятия решения о входе в "основной" блок кода, оба варианта ведут себя очень похоже благодаря укороченной логике. В первом случае, если Документ.Организация = "тест" будет Ложь, второе условие не проверится. Во втором случае, если Документ.Организация = "тест" будет Ложь, мы просто не войдем во внутренний Если, и второе условие также не проверится. Таким образом, в плане "вычисления всех операндов до конца", большой разницы не будет.
Фундаментальное и наиболее значимое различие между этими двумя подходами заключается в том, как мы можем обрабатывать ситуации, когда условия не выполняются, то есть в структуре и поведении блока ИНАЧЕ.
ИПри использовании конструкции Если ... И ... Тогда, у нас есть только один блок ИНАЧЕ. Этот блок сработает, если хотя бы одно из условий оказалось ложным.
Если Документ.Организация = "тест" И Документ.Подразделение = "Тестовое подразделение" Тогда
// Здесь выполняются действия, когда оба условия Истина
Сообщить("Организация и подразделение корректны.");
ИНАЧЕ
// Этот блок сработает, если Организация НЕ "тест"
// ИЛИ если Подразделение НЕ "Тестовое подразделение"
// (или оба условия ложны)
Сообщить("Неправильная организация ИЛИ подразделение. Что-то не то.");
КонецЕсли;
В этом случае, если мы хотим сообщить пользователю, что именно не так, мы не сможем сделать это напрямую из блока ИНАЧЕ, не проведя дополнительные проверки внутри него. Мы получаем общее сообщение о том, что "что-то не то", но не можем точно указать, какое именно условие не выполнилось.
ЕслиВ случае с вложенными операторами Если, мы получаем возможность иметь отдельные блоки ИНАЧЕ для каждого уровня условия. Это позволяет нам более точно реагировать на то, какое именно условие не выполнилось, и предоставлять более специфическую обратную связь или выполнять разную логику.
Если Документ.Организация = "тест" Тогда
// Если организация корректна, проверяем подразделение
Если Документ.Подразделение = "Тестовое подразделение" Тогда
// Здесь выполняются действия, когда оба условия Истина
Сообщить("Организация и подразделение корректны.");
ИНАЧЕ
// Этот блок сработает только если Организация корректна,
// но Подразделение НЕ "Тестовое подразделение"
Сообщить("Неправильное подразделение.");
КонецЕсли;
ИНАЧЕ
// Этот блок сработает только если Организация НЕ "тест"
Сообщить("Неправильная организация.");
КонецЕсли;
Как мы видим, этот подход предоставляет гораздо большую гибкость. Мы можем четко разделить логику обработки ошибок или альтернативных сценариев в зависимости от того, на каком этапе условие перестало выполняться.
Часто возникает вопрос об оптимальности кода: какой из вариантов быстрее или требует меньше ресурсов? На форумах 1С нередко можно встретить совет: "Не занимайтесь преждевременной оптимизацией."
Для простых условий, как в нашем примере, разница в производительности между этими двумя конструкциями обычно минимальна, если не сказать несущественна. Современные компиляторы и интерпретаторы (включая платформу 1С) хорошо оптимизируют подобные логические операции. Укороченная логика работает одинаково эффективно в обоих случаях для определения того, нужно ли входить в основной блок кода.
Фокусироваться в первую очередь следует на читаемости, поддерживаемости и корректности работы кода, а не на микрооптимизациях, которые могут дать прирост в миллисекундах, но при этом усложнить понимание и отладку кода. Если только у нас нет доказательств, что именно этот участок кода является "узким местом" по производительности, нет смысла тратить время на его "выжимание" до максимума в ущерб понятности.
Выбор между двумя конструкциями часто сводится к вопросу читаемости и поддерживаемости кода.
Когда необходимо выполнить одно действие при одновременном выполнении нескольких условий, и при этом не требуется специфическая реакция на каждое несработавшее условие, использование оператора И в одной строке обычно делает код более лаконичным и легким для чтения.
// Коротко и понятно, если не нужна детализация ошибок
Если Документ.Организация = "НашеПредприятие" И Документ.Проведен Тогда
ОбновитьСтатусДокумента();
КонецЕсли;
Такой подход предпочтителен, когда "условие выполнения одно" и не требуется ветвление логики для каждого невыполненного подусловия.
Если нам нужны разные действия или специфическая обратная связь в зависимости от того, какое из условий не выполнилось, вложенные Если с отдельными блоками ИНАЧЕ являются более подходящим вариантом. Они позволяют явно разделить логику обработки каждой ветки и предоставить пользователю или системе точную информацию о причине отказа.
// Детализированная обработка ошибок
Если Пользователь.Роль.Содержит("Администратор") Тогда
Если ДатаДокумента > ТекущаяДата() Тогда
Если СуммаДокумента > 0 Тогда
// Выполняем действия администратора с будущим документом и положительной суммой
ИНАЧЕ
Сообщить("Сумма документа должна быть больше нуля!");
КонецЕсли;
ИНАЧЕ
Сообщить("Дата документа не может быть в будущем!");
КонецЕсли;
ИНАЧЕ
Сообщить("У вас недостаточно прав для выполнения этой операции.");
КонецЕсли;
Однако, здесь есть и обратная сторона: глубокая вложенность операторов Если может ухудшать читаемость кода и увеличивать его сложность (так называемая "цикломатическая сложность"). Статический анализ кода часто "наказывает" за каждый новый уровень вложенности, так как это затрудняет понимание нормального хода выполнения программы и усложняет отладку.
Для улучшения читаемости и уменьшения вложенности, особенно когда у нас много предварительных проверок, мы можем использовать так называемые "защитные условия" (guard clauses). Этот подход заключается в том, чтобы выносить проверки граничных или специальных случаев в отдельные операторы Если в начале функции или процедуры. При срабатывании такого условия происходит немедленный выход (например, с помощью Возврат или Продолжить, если это цикл).
Рассмотрим, как это работает на примере:
// Вместо глубокой вложенности:
Процедура ОбработатьДокумент(ДокументСсылка)
Документ = ДокументСсылка.ПолучитьОбъект();
Если Не ЗначениеЗаполнено(Документ) Тогда
Сообщить("Документ не найден.");
Возврат;
КонецЕсли;
Если Документ.Проведен Тогда
Сообщить("Документ уже проведен.");
Возврат;
КонецЕсли;
Если Документ.Дата < НачалоГода(ТекущаяДата()) Тогда
Сообщить("Нельзя обрабатывать документы прошлых лет.");
Возврат;
КонецЕсли;
// Если все защитные условия пройдены, переходим к основной логике
// Код основной логики находится на более "плоском" уровне вложенности
Сообщить("Документ успешно прошел все проверки, выполняем основную логику.");
Документ.Записать();
КонецПроцедуры
Этот подход позволяет "спрямить" основной поток выполнения кода, делая его более линейным и понятным. Основная логика выполняется только тогда, когда все предварительные условия успешно пройдены.
Необходимо также помнить о приоритете логических операций в 1С при вычислении сложных логических выражений. 1С соблюдает стандартный порядок:
НЕ.И.ИЛИ.Для изменения порядка выполнения операций, а также для улучшения читаемости сложных выражений, всегда рекомендуется и нужно использовать круглые скобки. Например, Если (Условие1 И Условие2) ИЛИ Условие3 Тогда явно указывает на порядок выполнения, даже если он совпадает с приоритетом по умолчанию.
В 1С, как и во многих других языках программирования, логическое выражение – это выражение, результатом вычисления которого является значение типа Булево (Истина или Ложь). Эти значения являются основой для всех условных операторов и могут использоваться в других булевых операциях, например, для присвоения результата переменной:
Перем РезультатПроверки;
РезультатПроверки = (Документ.Организация = "тест" И Документ.Подразделение = "Тестовое подразделение");
Если РезультатПроверки Тогда
Сообщить("Все условия выполнены.");
Иначе
Сообщить("Одно или несколько условий не выполнены.");
КонецЕсли;
Этот подход может быть полезен, когда нам нужно сохранить результат проверки для дальнейшего использования или передать его в другую функцию.
Итак, давайте суммируем наши выводы и дадим конкретные рекомендации по выбору между двумя подходами:
Если ... И ... Тогда:
Если ... Тогда Если ... Тогда:
В целом, всегда стремитесь к созданию читаемого и поддерживаемого кода. Производительность в большинстве случаев не будет существенным фактором, поэтому ваш выбор должен основываться на ясности логики и простоте будущих изменений.