Как устроены типы данных в 1С 8.3: от Числа и Строки до ссылок и коллекций
Содержание
Тип значения в 1С — это то, чем является конкретное значение: числом, строкой, датой, ссылкой на элемент справочника, массивом и так далее. Узнать тип в коде можно функцией ТипЗнч(Значение), а сравнить его с нужным — через Тип("ИмяТипа"): Если ТипЗнч(Х) = Тип("Число") Тогда.
Типичная ситуация: отчёт падал у бухгалтера только на одном документе. Оказалось, в реквизите составного типа лежал не контрагент, а строка, и код обращался к её «наименованию». Понимание типов значений в 1С снимает такие ошибки ещё до запуска. Всё ниже относится к платформе 1С:Предприятие 8.3 и одинаково работает в любой конфигурации — БП, ЗУП, УТ или самописной.
Что такое тип значения и где он задаётся
У каждого значения в 1С есть ровно один тип. У переменной во встроенном языке типа нет: в одну и ту же переменную можно записать сначала число, потом строку. Язык динамический, тип проверяется во время выполнения.
А вот у реквизитов объектов метаданных тип задан жёстко. Разработчик выбирает его в Конфигураторе, в свойстве Тип реквизита. Там же задаются квалификаторы:
- 🔢 для числа — длина и точность (сколько знаков всего и после запятой), признак «неотрицательное»;
- 🔤 для строки — длина и вид длины: фиксированная или переменная; длина
0означает неограниченную строку; - 📅 для даты — состав: только дата, только время или дата и время.
Пользователь сталкивается с типами, даже не открывая Конфигуратор. Когда в поле «Субконто» или «Документ расчётов» сначала появляется окно «Выбор типа данных», это и есть реквизит составного типа: программа спрашивает, какой именно объект туда положить.
Как узнать тип значения в коде
Три инструмента, которые нужны постоянно:
| Функция / объект | Что делает | Пример |
|---|---|---|
ТипЗнч() | возвращает тип переданного значения | ТипЗнч(Контрагент) |
Тип() | получает тип по имени | Тип("СправочникСсылка.Контрагенты") |
ОписаниеТипов | описывает набор допустимых типов с квалификаторами | Реквизит.Тип.СодержитТип(...) |
Если ТипЗнч(Значение) = Тип("Строка") Тогда
Сообщить("Строка длиной " + СтрДлина(Значение));
ИначеЕсли ТипЗнч(Значение) = Тип("Число") Тогда
Сообщить("Число: " + Формат(Значение, "ЧГ=0"));
КонецЕсли;
В отладчике тип любого выражения виден в окне «Вычислить выражение» (Shift+F9) — в колонке «Тип». Это быстрее, чем дописывать Сообщить().
Примитивные типы данных в 1С
Примитивные типы хранят само значение, а не ссылку на объект. Их немного, и знать их нужно наизусть.
| Тип | Пример литерала | Пустое значение | Что учесть |
|---|---|---|---|
| Число | 125.50 | 0 | дробная часть отделяется точкой |
| Строка | "Счёт №15" | "" | кавычки внутри строки удваиваются: """ООО""" |
| Дата | '20240115' | '00010101' | хранит и дату, и время с точностью до секунды |
| Булево | Истина, Ложь | Ложь | — |
| Неопределено | Неопределено | — | «значение не задано» |
| Null | Null | — | приходит в основном из запросов |
| Тип | Тип("Число") | — | значение-тип, результат ТипЗнч() |
Число
Дробная часть в литерале пишется через точку: 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() сразу в запросе. Пустые значения ссылок в условиях задаются через ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка).
Ошибки типов: как найти причину и исправить
Тексты ошибок, связанных с типами, узнаваемы. По ним сразу понятно, куда смотреть.
Число() пришла строка с буквами, пробелами или разделителем групп. Проверьте источник строки: часто это результат неявного приведения числа к строке с пробелами. Используйте ОписаниеТипов.ПривестиЗначение() или очистите строку через СтрЗаменить().Неопределено. Обычно это пустой реквизит составного типа или строка вместо ссылки. Добавьте проверку ТипЗнч() перед обращением.Null с числом после запроса без ЕСТЬNULL(). Посмотрите, какой параметр указан в тексте ошибки.Порядок поиска одинаков для всех случаев:
- Откройте подробный текст ошибки и найдите строку модуля, где она возникла.
- Поставьте точку останова на этой строке и запустите сценарий в отладке.
- В окне
Shift+F9вычислитеТипЗнч()каждого участника выражения. - Найдите, откуда пришло значение «не того» типа: из реквизита, из запроса, из внешнего файла.
- Исправьте источник, а не только место падения: проверку типа добавьте там, где значение входит в систему.
Как ускорить работу с типами
Оптимизация здесь — в основном про то, чего не делать.
- 🐢 Широкий составной тип в реквизитах. Каждый лишний тип в составном реквизите — лишнее соединение в запросах по нему. Ограничивайте состав только реально нужными типами.
- 🐢 Неограниченные строки для ключевых полей. Артикул, ИНН, код из внешней системы — фиксированной или переменной длины. Иначе по ним не построится нормальный поиск, а в запросе понадобится
ВЫРАЗИТЬ. - 🐢
ПолучитьОбъект()ради одного реквизита. Чтение объекта целиком тянет все табличные части. Одно поле берите запросом. - 🐢 Обращение через точку в цикле.
Строка.Номенклатура.ЕдиницаИзмерениявнутри цикла по тысяче строк — тысяча скрытых обращений к базе. Соберите нужные данные одним запросом до цикла. - ⚡ Вызов
Тип()вне цикла. Если проверяете тип в большом цикле, получитеТип("...")один раз в переменную до цикла. - ⚡
Соответствиевместо поиска в таблице. Для частых проверок «есть ли ключ» соответствие по ссылке работает быстрее, чемНайтиСтроки()в цикле.
Для параметров процедур в конфигурациях на БСП есть ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(): он сразу сообщает, что передан не тот тип, вместо падения тремя вызовами глубже.
Типы значений при обмене данными: JSON и XML
Во внешний мир типы 1С уходят не все и не напрямую.
Что сериализуется в JSON без доработки
ЗаписатьJSON() напрямую принимает строки, числа, булево, Неопределено (станет null), дату, а также Массив, Структуру и Соответствие со строковыми ключами. Ссылки, таблицы значений и объекты так не записать — для них нужна функция преобразования или ручная сборка структуры.
Обычная практика — передавать ссылку её уникальным идентификатором:
УИД = XMLСтрока(Контрагент); // строка вида "b4e3a1c2-..."
Ссылка = XMLЗначение(Тип("СправочникСсылка.Контрагенты"), УИД);
По XMLЗначение() ссылка восстанавливается даже если такого элемента в базе нет — получится «битая» ссылка. После загрузки проверяйте её существование запросом.
Даты и числа на стыке систем
| Тип 1С | Что учесть при обмене |
|---|---|
| Дата | часовой пояс: 1С хранит дату без пояса; договоритесь, в каком поясе передаёте |
| Число | разделитель дробной части в выгрузке — точка; не форматируйте через Строка() |
| Строка | кодировка файла: для обмена — UTF-8 |
| Булево | во внешних системах бывает "1"/"0" или "Y"/"N" — приводите явно |
Для обменов через XDTO и правила конвертации типы описываются пакетами и правилами, а сопоставление идёт по описанию типов в схеме.
Частые вопросы
Чем Тип отличается от ТипЗнч?
ТипЗнч(Х) возвращает тип конкретного значения. Тип("Имя") возвращает тип по его имени. Чаще всего их сравнивают друг с другом.Как проверить, что в реквизите составного типа вообще что-то выбрано?
Неопределено и проверьте ЗначениеЗаполнено(Значение) — функция вернёт Ложь и для пустой ссылки, и для Неопределено.Можно ли передать объект с клиента на сервер?
Массив, Структура и Соответствие передаются свободно, а ТаблицаЗначений на клиенте недоступна.Почему ЗначениеЗаполнено(0) возвращает Ложь?
Как узнать, какие типы допустимы у реквизита?
Метаданные.Справочники.ИмяСправочника.Реквизиты.ИмяРеквизита.Тип.Типы(). Метод вернёт массив типов, входящих в описание.