Почему ПустоеЗначение() в 1С 7.7 возвращает ложный результат для переменной справочника и как это исправить?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

В процессе разработки и поддержки конфигураций на платформе 1С:Предприятие 7.7 программисты часто сталкиваются с неочевидным поведением встроенного языка при работе с агрегатными типами данных. Один из классических и наиболее запутанных сценариев связан с использованием системной функции ПустоеЗначение() для проверки элементов справочника, полученных программным путем.

Представим типичную ситуацию: вы программно создаете объект справочника, спозиционируете его на определенной записи (например, выбрав первый элемент), и пытаетесь проверить, не пуст ли он. Метод объекта Выбран() возвращает единицу (что означает — элемент успешно выбран), но системная функция ПустоеЗначение() при этом возвращает истину, как будто переменная пуста. В этой статье мы детально разберем внутренние механизмы платформы 1С 7.7, выясним причину такого поведения и рассмотрим правильные варианты написания кода.

Анализ проблемы на практическом примере

Для начала воспроизведем ситуацию и посмотрим на исходный код, который вызывает недоумение у многих разработчиков:


Контрагент = СоздатьОбъект("Справочник.Контрагенты");    
Контрагент.ВыбратьЭлементы();
Контрагент.ПолучитьЭлемент();

Сообщить("Получили первый элемент справочника.");
Сообщить("Значение переменной Контрагент:");
Сообщить(Контрагент); // Выведет наименование контрагента

Сообщить("Метод ПустоеЗначение(Контрагент) возвращает:");
Сообщить(ПустоеЗначение(Контрагент)); // Возвращает 1 (Истина) - почему?!

Сообщить("Метод Контрагент.Выбран() возвращает:");
Сообщить(Контрагент.Выбран()); // Возвращает 1 (Истина)

На первый взгляд, поведение платформы выглядит парадоксально. Объект успешно спозиционирован на записи базы данных, на экран выводится корректное наименование контрагента, метод Выбран() подтверждает наличие данных, но ПустоеЗначение() настойчиво утверждает, что переменная пуста. Чтобы понять, почему так происходит, нам необходимо обратиться к архитектуре хранения типов данных в 1С 7.7.

Разбираем понятия: Объект-курсор против Ссылки-значения

В платформе 1С:Предприятие 7.7 существует жесткое разделение между инструментами работы с базой данных и самими данными. Переменная, созданная через конструкцию СоздатьОбъект(), и реквизит формы (диалога) — это две принципиально разные сущности, хотя внешне они могут вести себя похоже.

Рассмотрим эти сущности подробнее:

  1. Объект-курсор (манипулятор): Когда мы пишем СоздатьОбъект("Справочник.Контрагенты"), мы создаем в оперативной памяти специальную программную оболочку. Это контекст, предназначенный для поиска, выборки, обхода и редактирования записей в базе данных. Сам этот объект-манипулятор **не является конкретным значением данных** — это лишь инструмент (курсор) для доступа к ним.
  2. Ссылка на элемент (значение): Это уникальный идентификатор конкретной записи в таблице базы данных. Именно ссылка записывается в реквизиты документов, другие справочники или выбирается пользователем непосредственно в диалоговых формах. Ссылка — это физическое значение, которое хранится в базе.

Теперь сопоставим это с работой системных функций:

Функция ПустоеЗначение() проверяет именно **значение данных** (переменную базового типа или объектную ссылку). Когда мы передаем в эту функцию переменную-курсор Контрагент (созданную через СоздатьОбъект), функция видит системный объект-манипулятор, а не конкретную ссылку на элемент справочника. С точки зрения системы хранения данных, сам этот манипулятор не является ссылкой, а значит, для функции ПустоеЗначение() он всегда пуст. Поэтому она возвращает 1 (Истина).

В то же время метод Выбран() — это внутреннее свойство самого объекта-курсора. Он проверяет состояние этого курсора в текущий момент времени: указывает ли он сейчас на какую-либо реальную физическую запись в базе данных или нет. Поскольку мы выполнили ПолучитьЭлемент(), курсор успешно указывает на запись, и метод Выбран() возвращает 1.

Почему возникает иллюзия одинаковости?

Разработчиков часто вводит в заблуждение тот факт, что в некоторых контекстах объект-курсор и ссылка ведут себя абсолютно идентично. Выделим две главные причины этой иллюзии:

Во-первых, неявное преобразование типов. Когда мы пишем Сообщить(Контрагент), система понимает, что для вывода на экран объект необходимо преобразовать в строку. В этот момент платформа неявно обращается к текущей записи, на которую указывает курсор, извлекает ссылку, берет ее наименование и выводит на экран. Это создает ложное ощущение, что в переменной Контрагент лежит обычное значение справочника.

Во-вторых, ограничения функции ТипЗначенияСтр(). Если мы попытаемся программно проверить тип переменной-курсора и тип переменной-ссылки:


Сообщить(ТипЗначенияСтр(Контрагент)); // Возвратит "Справочник"
Сообщить(ТипЗначенияСтр(Контрагент.ТекущийЭлемент())); // Тоже возвратит "Справочник"!

Платформа скрывает внутренние различия типов на уровне этой функции, возвращая для обеих сущностей строку "Справочник". Это сильно затрудняет отладку и понимание происходящего для начинающих разработчиков.

Решения проблемы: как писать код правильно?

Поскольку мы выяснили первопричину, давайте рассмотрим, как правильно решать подобные задачи на практике в зависимости от контекста.

Вариант 1. Использование метода ТекущийЭлемент()

Если вам необходимо передать значение из объектной переменной в универсальную процедуру (например, расположенную в Глобальном модуле), которая внутри использует функцию ПустоеЗначение(), вам обязательно нужно извлечь из объекта-курсора конкретную ссылку на элемент. Для этого используется метод ТекущийЭлемент().

Рассмотрим корректный вариант кода:


Контрагент = СоздатьОбъект("Справочник.Контрагенты");    
Контрагент.ВыбратьЭлементы();
Если Контрагент.ПолучитьЭлемент() = 1 Тогда
    // Получаем конкретную ссылку на текущий элемент выборки
    СсылкаНаКонтрагента = Контрагент.ТекущийЭлемент();
    
    // Теперь функция ПустоеЗначение отработает абсолютно корректно!
    Если ПустоеЗначение(СсылкаНаКонтрагента) = 0 Тогда
        Сообщить("Элемент успешно проверен и он не пустой: " + СсылкаНаКонтрагента);
    КонецЕсли;
КонецЕсли;

Вариант 2. Проверка через метод Выбран()

Если проверка осуществляется локально (в рамках того же программного блока, где был создан объект), и вам не нужно передавать переменную во внешние функции общего назначения, правильнее и быстрее использовать родной метод объекта-курсора:


Контрагент = СоздатьОбъект("Справочник.Контрагенты");
// ... какие-то действия по поиску или выборке контрагента ...

Если Контрагент.Выбран() = 1 Тогда
    // Объект спозиционирован на реальном элементе, можем работать с его реквизитами
    Сообщить("Успешно выбран контрагент: " + Контрагент.Наименование);
Иначе
    Сообщить("Элемент справочника не выбран или не найден!");
КонецЕсли;

Вариант 3. Сравнение с пустым значением конкретного типа

Иногда требуется проверить, совпадает ли полученный элемент с пустой ссылкой данного справочника. Для этого мы можем сравнить полученную ссылку с системным пустым шаблоном, который возвращает функция ПолучитьПустоеЗначение():


Контрагент = СоздатьОбъект("Справочник.Контрагенты");
// ... позиционирование объекта ...

СсылкаНаЭлемент = Контрагент.ТекущийЭлемент();
ПустойКонтрагент = ПолучитьПустоеЗначение("Справочник.Контрагенты");

Если СсылкаНаЭлемент = ПустойКонтрагент Тогда
    Сообщить("Элемент является пустой ссылкой справочника Контрагенты");
КонецЕсли;

Аналогия с платформой 1С:Предприятие 8

Для разработчиков, которые больше знакомы с объектной моделью 1С версии 8.x, провести аналогию будет очень просто. В версии 8.x эти сущности были четко разделены на уровне типов данных, что исключило подобную путаницу:

В 1С 8.x вы никогда не сможете передать саму выборку в функцию проверки заполнения значения так, чтобы она вернула адекватный результат проверки конкретной записи, пока не обратитесь к свойству Ссылка текущей записи выборки. В 1С 7.7 действует абсолютно та же логика, но скрытая за одинаковыми визуальными представлениями типов данных.

Применяя метод ТекущийЭлемент() при работе с объектными переменными справочников и документов перед их анализом в функциях типа ПустоеЗначение(), вы обезопасите свой код от трудночитаемых ошибок и обеспечите его стабильную и предсказуемую работу.

← На главную