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