В практике разработки на платформе 1С:Предприятие 8 часто возникает ситуация, когда один и тот же программный код ведет себя по-разному в зависимости от прав доступа пользователя. Одной из наиболее неочевидных проблем является ошибка «Преобразование значения к типу Булево не может быть выполнено», возникающая внутри логических конструкций Если ... Тогда или при присваивании логического значения переменной. Рассмотрим подробнее, почему выражение, которое успешно работает у администратора, может приводить к аварийному завершению сеанса у обычного пользователя.
Для начала разберем фундаментальную особенность компилятора встроенного языка 1С — ленивые вычисления (или оптимизация логических выражений). Проанализируем, как система обрабатывает составные условия с операторами И и ИЛИ.
Когда платформа встречает оператор ИЛИ, она вычисляет операнды слева направо. Если первый операнд равен Истина, то результат всего выражения гарантированно будет истинным, независимо от того, что стоит дальше. В целях оптимизации 1С просто прекращает дальнейшие вычисления. Если же первый операнд равен Ложь, система обязана вычислить следующий фрагмент условия.
Рассмотрим это на примере кода из обсуждения:
Флаг = РольДоступна("ПолныеПрава") Или П1 Или Ф1;
Проанализируем ситуацию для двух разных пользователей:
РольДоступна("ПолныеПрава") возвращает значение Истина. Платформа видит, что первое условие в цепочке ИЛИ выполнено, и сразу возвращает результат Истина. Переменные П1 и Ф1 даже не запрашиваются из памяти и не проверяются. Если в них в этот момент находится Неопределено, ошибка не возникнет.РольДоступна() возвращает Ложь. Чтобы определить итоговое значение переменной Флаг, платформа вынуждена перейти ко второму операнду — П1. Если в П1 содержится значение типа Неопределено (например, реквизит не заполнен или функция поиска не вернула результат), система попытается применить к нему логическую операцию.Выясним причину, по которой 1С выдает ошибку. В отличие от некоторых других языков программирования (например, JavaScript), где значения undefined или 0 могут быть неявно приведены к false, встроенный язык 1С придерживается строгой типизации для логических операций. Операндами для И, ИЛИ, НЕ могут выступать только значения типа Булево.
Когда система доходит до вычисления переменной П1, имеющей тип Неопределено, в контексте логического выражения, она пытается привести это значение к Булево. Поскольку автоматическое преобразование Неопределено -> Ложь в языке не предусмотрено, генерируется исключение: «Преобразование значения к типу Булево не может быть выполнено».
Посмотрим на примеры из практики:
// Пример 1: Ошибки не будет, так как вычисление остановится на первом шаге
Результат = Истина Или Неопределено;
// Пример 2: Ошибка возникнет, так как системе нужно вычислить второй операнд
Результат = Ложь Или Неопределено;
// Пример 3: Ошибка возникнет сразу
Результат = Неопределено Или Истина;
Чтобы избежать зависимости стабильности системы от данных или прав пользователя, необходимо гарантировать, что в логическом выражении участвуют только переменные типа Булево. Разберем несколько надежных способов реализации этой задачи.
Самый простой и читаемый способ — это всегда дописывать = Истина при проверке переменных, в которых вы не уверены. Проанализируем трансформацию кода:
// Было (опасно):
Флаг = РольДоступна("ПолныеПрава") Или П1 Или Ф1;
// Стало (безопасно):
Флаг = РольДоступна("ПолныеПрава") Или (П1 = Истина) Или (Ф1 = Истина);
В данном случае, если П1 равно Неопределено, выражение (Неопределено = Истина) просто вернет Ложь типа Булево. Ошибки преобразования не будет, и алгоритм перейдет к следующей части условия.
Мы можем воспользоваться конструктором типа Булево(). Это полезно, когда нужно гарантированно получить логический тип из неопределенных данных.
// Принудительно превращаем всё в Булево
Флаг = РольДоступна("ПолныеПрава") Или Булево(П1) Или Булево(Ф1);
Функция Булево(Неопределено) всегда возвращает Ложь. Аналогично она сработает и для числовых значений (0 станет Ложь, остальное — Истина).
Если логика вашего приложения подразумевает, что Неопределено — это отсутствие значения, которое нельзя трактовать как Ложь без предварительной проверки, используйте функцию ЗначениеЗаполнено().
Если ЗначениеЗаполнено(П1) Тогда
// Здесь мы уверены, что П1 имеет смысл проверять
Флаг = П1 Или Ф1;
КонецЕсли;
Важно не путать поведение кода во встроенном языке и поведение языка запросов 1С. В запросах (на уровне SQL) используется трехзначная логика. Если в запросе вы напишете ГДЕ Поле1 ИЛИ Поле2, и в одном из полей будет значение NULL, запрос не «упадет». Он вернет результат в соответствии с таблицами истинности SQL, где TRUE OR NULL = TRUE. Однако в программном коде 1С такая вольность недопустима.
Подведем итоги нашего анализа. Проблема «разного поведения под ролями» — это лишь следствие того, что у разных пользователей функции возвращают разные значения, что заставляет платформу вычислять или игнорировать последующие части логической цепи. Чтобы ваш код был надежным:
ИЛИ.Переменная = Истина, если существует малейший шанс попадания в переменную значения Неопределено или NULL.Ложь в начале условия.Соблюдение этих простых правил позволит избежать трудноуловимых ошибок, которые проявляются только у конечных пользователей в процессе эксплуатации системы.