Как устроены типы данных в 1С 8.3: от Числа и Строки до ссылок и коллекций

· 10 мин

Содержание

Тип значения в 1С — это то, чем является конкретное значение: числом, строкой, датой, ссылкой на элемент справочника, массивом и так далее. Узнать тип в коде можно функцией ТипЗнч(Значение), а сравнить его с нужным — через Тип("ИмяТипа"): Если ТипЗнч(Х) = Тип("Число") Тогда.

Типичная ситуация: отчёт падал у бухгалтера только на одном документе. Оказалось, в реквизите составного типа лежал не контрагент, а строка, и код обращался к её «наименованию». Понимание типов значений в 1С снимает такие ошибки ещё до запуска. Всё ниже относится к платформе 1С:Предприятие 8.3 и одинаково работает в любой конфигурации — БП, ЗУП, УТ или самописной.

Что такое тип значения и где он задаётся

У каждого значения в 1С есть ровно один тип. У переменной во встроенном языке типа нет: в одну и ту же переменную можно записать сначала число, потом строку. Язык динамический, тип проверяется во время выполнения.

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

  • 🔢 для числа — длина и точность (сколько знаков всего и после запятой), признак «неотрицательное»;
  • 🔤 для строки — длина и вид длины: фиксированная или переменная; длина 0 означает неограниченную строку;
  • 📅 для даты — состав: только дата, только время или дата и время.

Пользователь сталкивается с типами, даже не открывая Конфигуратор. Когда в поле «Субконто» или «Документ расчётов» сначала появляется окно «Выбор типа данных», это и есть реквизит составного типа: программа спрашивает, какой именно объект туда положить.

Как узнать тип значения в коде

Три инструмента, которые нужны постоянно:

Функция / объектЧто делаетПример
ТипЗнч()возвращает тип переданного значенияТипЗнч(Контрагент)
Тип()получает тип по имениТип("СправочникСсылка.Контрагенты")
ОписаниеТиповописывает набор допустимых типов с квалификаторамиРеквизит.Тип.СодержитТип(...)
Если ТипЗнч(Значение) = Тип("Строка") Тогда
	Сообщить("Строка длиной " + СтрДлина(Значение));
ИначеЕсли ТипЗнч(Значение) = Тип("Число") Тогда
	Сообщить("Число: " + Формат(Значение, "ЧГ=0"));
КонецЕсли;

В отладчике тип любого выражения виден в окне «Вычислить выражение» (Shift+F9) — в колонке «Тип». Это быстрее, чем дописывать Сообщить().

Примитивные типы данных в 1С

Примитивные типы хранят само значение, а не ссылку на объект. Их немного, и знать их нужно наизусть.

ТипПример литералаПустое значениеЧто учесть
Число125.500дробная часть отделяется точкой
Строка"Счёт №15"""кавычки внутри строки удваиваются: """ООО"""
Дата'20240115''00010101'хранит и дату, и время с точностью до секунды
БулевоИстина, ЛожьЛожь—
НеопределеноНеопределено—«значение не задано»
NullNull—приходит в основном из запросов
ТипТип("Число")—значение-тип, результат ТипЗнч()

Число

Дробная часть в литерале пишется через точку: 0.2, а не 0,2. Операции с числами точные, без погрешностей плавающей точки, поэтому 0.1 + 0.2 = 0.3 даёт Истина. Деньги в 1С считают без страха копеечных «хвостов» — округление делается явно функцией Окр(Сумма, 2).

Строка

Строку можно переносить на несколько строк кода символом | в начале продолжения. Для многострочного текста удобнее СтрШаблон() и Символы.ПС.

Длина строки в реквизите влияет на хранение. Фиксированная строка дополняется пробелами до заданной длины, переменная — нет. Неограниченная строка (длина 0) хранится иначе, и в запросах с ней есть ограничения — о них ниже.

Дата

Тип «Дата» в 1С всегда хранит и дату, и время. Квалификатор «Состав даты» лишь ограничивает, что видит пользователь и что записывается в реквизит. Пустая дата — '00010101000000', проверять её удобнее через ЗначениеЗаполнено() или сравнением с Дата(1, 1, 1).

Арифметика простая: к дате прибавляют секунды.

Завтра = ТекущаяДатаСеанса() + 86400;     // плюс сутки
Разница = (ДатаОкончания - ДатаНачала) / 86400; // разница в днях

Неопределено и Null — не одно и то же

Их путают чаще всего:

  • ⚪ Неопределено — «значение не выбрано». Это пустое значение реквизита составного типа, результат функции без возврата, отсутствующий ключ в Соответствии при обращении через Получить().
  • ⚫ Null — «значения нет вообще». Появляется в результате запроса: при левом соединении, когда справа нет строки, или при обращении к реквизиту через точку от пустой ссылки.

Null нельзя сложить с числом — получите ошибку. Поэтому в запросах его гасят через ЕСТЬNULL(Поле, 0).

Ссылочные, объектные и универсальные типы

Сверх примитивов платформа создаёт типы под каждый объект конфигурации. Добавили справочник «Склады» — появились типы СправочникСсылка.Склады, СправочникОбъект.Склады, СправочникМенеджер.Склады и другие.

Ссылка и объект

ЧтоТипДля чего
СсылкаДокументСсылка.ПоступлениеТоваровУслугхранится в реквизитах, передаётся между клиентом и сервером, читается в запросах
ОбъектДокументОбъект.ПоступлениеТоваровУслугизменение и запись: Ссылка.ПолучитьОбъект()
МенеджерДокументМенеджер.ПоступлениеТоваровУслугсоздание, поиск, выборка: Документы.ПоступлениеТоваровУслуг

Ссылка — лёгкое значение, по сути идентификатор записи. Объект — тяжёлое: при ПолучитьОбъект() читаются все реквизиты и табличные части. Если нужно одно поле, берите его запросом или через ОбщегоНазначения.ЗначениеРеквизитаОбъекта() в конфигурациях на БСП.

Пустая ссылка — отдельное значение каждого ссылочного типа. Проверяется методом Ссылка.Пустая() или сравнением с Справочники.Склады.ПустаяСсылка().

Универсальные коллекции

КоллекцияКогда брать
Массивупорядоченный список значений, доступ по индексу с нуля
Структуранабор именованных полей, ключ — строка-идентификатор
Соответствиеключ любого типа: ссылка, число, дата
СписокЗначенийсписок с представлениями и пометками, удобен для выбора на форме
ТаблицаЗначенийстроки и колонки, группировка через Свернуть(), поиск через НайтиСтроки()
ДеревоЗначенийиерархические данные

У массива, структуры и соответствия есть фиксированные варианты: ФиксированныйМассив, ФиксированнаяСтруктура, ФиксированноеСоответствие. Их нельзя изменить, поэтому их удобно отдавать из повторно используемых модулей.

Составной тип

Реквизит может допускать сразу несколько типов. Классический пример — Регистратор регистра: в нём лежит ссылка на любой документ, который делает движения. Пустое значение составного реквизита — Неопределено, а не пустая ссылка.

Перед обращением к полям значения составного типа проверяйте тип:

Если ТипЗнч(Строка.Субконто1) = Тип("СправочникСсылка.Контрагенты") Тогда
	ИНН = Строка.Субконто1.ИНН;
КонецЕсли;

Приведение типов: явное и неявное

Неявное приведение

Платформа приводит тип сама в арифметике и сложении строк. Правило: результат определяется левым операндом.

Текст = "Сумма: " + 1500;   // "Сумма: 1 500" — число стало строкой с разделителем групп
Итог  = 10 + "5";           // 15 — строка "5" приведена к числу
Срок  = '20240101' + 3600;  // 01.01.2024 01:00:00

Первая строка — частая ловушка. Неявное приведение числа к строке форматирует его, и тысячи отделяются неразрывным пробелом. В имени файла или в коде для обмена это ломает данные.

Явное приведение

ФункцияПримерРезультат
Число()Число("125.5")125.5
Строка()Строка(Истина)"Да"
Дата()Дата("20240115") или Дата(2024, 1, 15)15.01.2024 0:00:00
Булево()Булево(1)Истина
Формат()Формат(1500, "ЧГ=0")"1500" без пробела

Если строку нельзя превратить в число, Число() вызывает исключение. Для пользовательского ввода и данных из файлов надёжнее ОписаниеТипов.ПривестиЗначение(): оно не падает, а возвращает пустое значение типа.

ОписаниеЧисла = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2));
Сумма = ОписаниеЧисла.ПривестиЗначение("abc");    // 0, без исключения

ПривестиЗначение() молча превращает мусор в 0. Для загрузки из Excel это удобно, но строку с неверной суммой стоит отдельно выводить в лог, иначе в учёт уйдёт нулевая сумма.

Типы значений в запросах 1С

Язык запросов работает с типами по своим правилам. Здесь три приёма, которые нужны в любой нетривиальной выборке.

ТИПЗНАЧЕНИЯ, ТИП и ССЫЛКА

Отбор по типу составного поля:

ВЫБРАТЬ
	Хозрасчетный.Регистратор
ИЗ
	РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
ГДЕ
	Хозрасчетный.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг

То же можно записать как ТИПЗНАЧЕНИЯ(Хозрасчетный.Регистратор) = ТИП(Документ.РеализацияТоваровУслуг). Вариант с ССЫЛКА короче и читается легче.

ВЫРАЗИТЬ — для скорости и для строк

Когда вы пишете Регистратор.Номер, а регистратор может быть двадцатью видами документов, платформа строит соединения со всеми двадцатью таблицами. Запрос замедляется в разы. Если нужен один вид, приведите поле явно:

ВЫРАЗИТЬ(Хозрасчетный.Регистратор КАК Документ.РеализацияТоваровУслуг).Номер

Второе применение — строки неограниченной длины. Их нельзя сравнивать, группировать и упорядочивать напрямую. Обрезка решает проблему:

ГДЕ ВЫРАЗИТЬ(Док.Комментарий КАК СТРОКА(200)) = &Текст

Null в результате запроса

После левого соединения отсутствующие значения приходят как Null, а не как 0 или пустая ссылка. Оборачивайте такие поля в ЕСТЬNULL() сразу в запросе. Пустые значения ссылок в условиях задаются через ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка).

Ошибки типов: как найти причину и исправить

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

Диагностика: выберите симптом

Порядок поиска одинаков для всех случаев:

  1. Откройте подробный текст ошибки и найдите строку модуля, где она возникла.
  2. Поставьте точку останова на этой строке и запустите сценарий в отладке.
  3. В окне Shift+F9 вычислите ТипЗнч() каждого участника выражения.
  4. Найдите, откуда пришло значение «не того» типа: из реквизита, из запроса, из внешнего файла.
  5. Исправьте источник, а не только место падения: проверку типа добавьте там, где значение входит в систему.

Как ускорить работу с типами

Оптимизация здесь — в основном про то, чего не делать.

  • 🐢 Широкий составной тип в реквизитах. Каждый лишний тип в составном реквизите — лишнее соединение в запросах по нему. Ограничивайте состав только реально нужными типами.
  • 🐢 Неограниченные строки для ключевых полей. Артикул, ИНН, код из внешней системы — фиксированной или переменной длины. Иначе по ним не построится нормальный поиск, а в запросе понадобится ВЫРАЗИТЬ.
  • 🐢 ПолучитьОбъект() ради одного реквизита. Чтение объекта целиком тянет все табличные части. Одно поле берите запросом.
  • 🐢 Обращение через точку в цикле. Строка.Номенклатура.ЕдиницаИзмерения внутри цикла по тысяче строк — тысяча скрытых обращений к базе. Соберите нужные данные одним запросом до цикла.
  • ⚡ Вызов Тип() вне цикла. Если проверяете тип в большом цикле, получите Тип("...") один раз в переменную до цикла.
  • ⚡ Соответствие вместо поиска в таблице. Для частых проверок «есть ли ключ» соответствие по ссылке работает быстрее, чем НайтиСтроки() в цикле.

Для параметров процедур в конфигурациях на БСП есть ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(): он сразу сообщает, что передан не тот тип, вместо падения тремя вызовами глубже.

Типы значений при обмене данными: JSON и XML

Во внешний мир типы 1С уходят не все и не напрямую.

Что сериализуется в JSON без доработки

ЗаписатьJSON() напрямую принимает строки, числа, булево, Неопределено (станет null), дату, а также Массив, Структуру и Соответствие со строковыми ключами. Ссылки, таблицы значений и объекты так не записать — для них нужна функция преобразования или ручная сборка структуры.

Обычная практика — передавать ссылку её уникальным идентификатором:

УИД = XMLСтрока(Контрагент);  // строка вида "b4e3a1c2-..."
Ссылка = XMLЗначение(Тип("СправочникСсылка.Контрагенты"), УИД);

По XMLЗначение() ссылка восстанавливается даже если такого элемента в базе нет — получится «битая» ссылка. После загрузки проверяйте её существование запросом.

Даты и числа на стыке систем

Тип 1СЧто учесть при обмене
Датачасовой пояс: 1С хранит дату без пояса; договоритесь, в каком поясе передаёте
Числоразделитель дробной части в выгрузке — точка; не форматируйте через Строка()
Строкакодировка файла: для обмена — UTF-8
Булевово внешних системах бывает "1"/"0" или "Y"/"N" — приводите явно

Для обменов через XDTO и правила конвертации типы описываются пакетами и правилами, а сопоставление идёт по описанию типов в схеме.

Частые вопросы

Чем Тип отличается от ТипЗнч?
ТипЗнч(Х) возвращает тип конкретного значения. Тип("Имя") возвращает тип по его имени. Чаще всего их сравнивают друг с другом.
Как проверить, что в реквизите составного типа вообще что-то выбрано?
Сравните значение с Неопределено и проверьте ЗначениеЗаполнено(Значение) — функция вернёт Ложь и для пустой ссылки, и для Неопределено.
Можно ли передать объект с клиента на сервер?
Нет, объект передаётся только в виде данных формы. Ссылки, примитивы, Массив, Структура и Соответствие передаются свободно, а ТаблицаЗначений на клиенте недоступна.
Почему ЗначениеЗаполнено(0) возвращает Ложь?
Ноль — пустое значение типа «Число». Если ноль в поле допустим, проверяйте тип и значение отдельно.
Как узнать, какие типы допустимы у реквизита?
В коде — через Метаданные.Справочники.ИмяСправочника.Реквизиты.ИмяРеквизита.Тип.Типы(). Метод вернёт массив типов, входящих в описание.