При написании кода во встроенном языке 1С разработчики часто сталкиваются с необходимостью выбрать первое заполненное или отличное от Неопределено (или NULL) значение из списка переменных. В классическом SQL для этого существует элегантная функция COALESCE, которая принимает неограниченное количество аргументов и возвращает первый из них, не равный NULL. В других языках программирования есть удобные операторы, такие как ?? в C# или оператор логического «ИЛИ» в JavaScript.
К сожалению, во встроенном языке 1С аналогичной нативной функции со свободной структурой аргументов нет. Конструкции вида Если...ИначеЕсли...КонецЕсли загромождают код и превращают его в трудночитаемую структуру («спагетти-код»). В этой статье мы подробно разберем, как обойти это ограничение платформы и какие альтернативные подходы используют практикующие разработчики 1С.
Для простых ситуаций, когда нам нужно проверить цепочку всего из двух-трех переменных, самым быстрым и лаконичным решением является использование тернарного (условного) оператора ?(). Этот оператор позволяет уместить ветвление в одну строку кода.
Давайте проанализируем пример, в котором нам нужно выбрать первое значение, отличное от Неопределено, среди трех переменных (Знач1, Знач2 и Знач3):
Результат = ?(Знач1 <> Неопределено, Знач1, ?(Знач2 <> Неопределено, Знач2, Знач3));
Преимущества данного подхода:
Недостатки:
Во встроенном языке 1С отсутствует поддержка функций с произвольным количеством аргументов (таких как ключевое слово params в C# или оператор ...args в современных языках). Чтобы передать в функцию динамическое число параметров для поиска первого заполненного значения, мы можем использовать стандартный коллекционный тип данных — Массив.
Давайте создадим универсальную функцию в общем модуле (например, ОбщегоНазначенияКлиентСервер), которая будет имитировать поведение COALESCE:
Функция COALESCE(МассивЗначений) Экспорт
Для Каждого ТекущееЗначение Из МассивЗначений Цикл
Если ТекущееЗначение <> Неопределено И ТекущееЗначение <> NULL Тогда
Возврат ТекущееЗначение;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
Вызов такой функции в коде будет выглядеть следующим образом:
ВыбранноеЗначение = ОбщегоНазначенияКлиентСервер.COALESCE(Новый Массив(Знач1, Знач2, Знач3, Знач4));
Этот способ максимально приближен к концепции COALESCE, так как позволяет передавать неограниченное количество параметров внутри конструктора массива. Однако помните, что инициализация нового массива на лету создает дополнительную нагрузку на память, поэтому такую конструкцию не рекомендуется вызывать внутри высоконагруженных циклов.
В бизнес-логике 1С часто требуется найти не просто значение, отличное от системных типов NULL или Неопределено, а именно заполненное значение. Например, пустая ссылка справочника (Справочники.Номенклатура.ПустаяСсылка()) или пустая строка ("") не равны Неопределено, но логически они являются пустыми.
Для решения этой задачи мы можем модифицировать нашу функцию, применив встроенный метод платформы ЗначениеЗаполнено(). Это позволит нам находить первое действительно значащее поле:
Функция ПервоеЗаполненноеЗначение(МассивЗначений) Экспорт
Для Каждого ТекущееЗначение Из МассивЗначений Цикл
Если ЗначениеЗаполнено(ТекущееЗначение) Тогда
Возврат ТекущееЗначение;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
Применение такой функции гарантирует, что пустые даты, пустые строки и пустые ссылки на объекты базы данных будут пропущены, и вы получите только первое реально заполненное значение.
Если вам не хочется каждый раз инициализировать массив при вызове функции, можно использовать альтернативный прием — объявить функцию с фиксированным, но достаточно большим количеством необязательных параметров. В 1С значения по умолчанию для параметров функции инициализируются ключевым словом Неопределено.
Разберем пример реализации такой функции:
Функция ВыбратьПервоеЗначение(Знач1, Знач2 = Неопределено, Знач3 = Неопределено, Знач4 = Неопределено, Знач5 = Неопределено) Экспорт
Если Знач1 <> Неопределено Тогда
Возврат Знач1;
ИначеЕсли Знач2 <> Неопределено Тогда
Возврат Знач2;
ИначеЕсли Знач3 <> Неопределено Тогда
Возврат Знач3;
ИначеЕсли Знач4 <> Неопределено Тогда
Возврат Знач4;
Иначе
Возврат Знач5;
КонецЕсли;
КонецФункции
Теперь вызов функции в коде выглядит очень лаконично и красиво:
Результат = ОбщегоНазначенияКлиентСервер.ВыбратьПервоеЗначение(Знач1, Знач2, Знач3);
Важное ограничение: этот метод жестко лимитирует количество проверяемых переменных (в нашем примере — максимум пять). Если вам понадобится проверить шестую переменную, придется переписывать саму функцию.
Стоит четко разграничивать встроенный язык 1С и язык запросов. В языке запросов 1С существует встроенная функция ЕСТЬНУЛЛ(), которая является близким родственником SQL-функции ISNULL().
Основное ограничение ЕСТЬНУЛЛ() заключается в том, что она принимает строго два параметра. Чтобы сымитировать поведение COALESCE для трех и более полей в запросе, нам придется использовать вложенные конструкции:
ВЫБРАТЬ
ЕСТЬНУЛЛ(Таблица.Поле1, ЕСТЬНУЛЛ(Таблица.Поле2, ЕСТЬНУЛЛ(Таблица.Поле3, "ДефолтноеЗначение"))) КАК РезультатВыбора
ИЗ
Справочник.Номенклатура КАК Таблица
Такой подход прекрасно оптимизируется СУБД на низком уровне и работает максимально быстро, поэтому при работе с выборками данных из СУБД всегда отдавайте предпочтение вложенным ЕСТЬНУЛЛ, а не постобработке результата в коде.
Мы проанализировали несколько способов реализации аналога функции COALESCE во встроенном языке 1С. Каждый из них имеет свои области применения:
?() идеален для быстрой проверки двух-трех локальных переменных на месте.ЗначениеЗаполнено().ЕСТЬНУЛЛ() — безальтернативный и наиболее производительный вариант при работе с запросами к базе данных.