При разработке сложных алгоритмов в среде "1С:Предприятие" мы регулярно сталкиваемся с необходимостью организации сложных структур данных. Стандартных механизмов, таких как линейные массивы или плоские таблицы значений, не всегда достаточно для реализации заложенной бизнес-логики. Возникает закономерный вопрос: позволяет ли функционал платформы 1С хранить многомерные структуры (например, двумерные массивы) внутри других универсальных коллекций, в частности, в объекте СписокЗначений?
В этой статье мы подробно разберем, как устроена работа со вложенными коллекциями в 1С, исправим типичные синтаксические ошибки начинающих разработчиков, проведем глубокий сравнительный анализ различных способов организации матричных данных, а также рассмотрим важные архитектурные ограничения, связанные с клиент-серверным взаимодействием и сериализацией данных.
Рассмотрим ситуацию, когда нам необходимо поместить массив массивов в СписокЗначений. В исходном коде, предложенном автором темы на форуме, содержалось несколько критических неточностей, из-за которых платформа выдавала ошибки компиляции. Проанализируем исходный код по шагам и выясним причины сбоев.
Вот как выглядела первоначальная некорректная инициализация:
СписокМасивов = Новый СписокЗначений; // Опечатка в имени переменной
Масс = Новый Массив(3);
ДвуМасс = Новый Массив;
ДвуМасс.Добавить(Масс);
СписокМасивов.Добавить("Имя", ДвуМасс); // Неверный порядок параметров метода Добавить()
Разберем, почему этот код не будет работать в реальной базе 1С:
СписокМасивов (с одной буквой "с"), что в дальнейшем привело бы к ошибке обращения к неопределенной переменной при строгом синтаксическом контроле.Добавить() у объекта СписокЗначений принимает параметры в следующем порядке: Добавить(Значение, Представление, Пометка, Картинка). Автор же попытался первым параметром передать строковое представление ("Имя"), а вторым — сам объект коллекции (ДвуМасс). В результате в качестве значения элемента списка была бы записана строка, а не массив, что полностью разрушает исходную логику.Давайте посмотрим на исправленный, полностью работоспособный вариант кода, который успешно пройдет синтаксический контроль платформы:
СписокМассивов = Новый СписокЗначений;
// Создаем внутренний массив (строку нашей будущей матрицы)
Масс = Новый Массив(3);
Масс[0] = "Значение 1";
Масс[1] = "Значение 2";
Масс[2] = "Значение 3";
// Создаем внешний массив (ступенчатый двумерный массив)
ДвуМасс = Новый Массив;
ДвуМасс.Добавить(Масс);
// Правильно добавляем полученную структуру в СписокЗначений
// Первый параметр - объект (ДвуМасс), второй - его строковое представление ("Имя")
СписокМассивов.Добавить(ДвуМасс, "Имя");
Платформа 1С без проблем позволяет выполнять подобные операции. Вложенность универсальных коллекций друг в друга практически не ограничена. Мы можем помещать структуру в структуру, массив в соответствие, список значений в дерево и так далее.
При проектировании многомерных матриц в 1С важно понимать разницу между двумя принципиально отличающимися подходами к их созданию. Разработчики часто путают ступенчатые (зубчатые) массивы и истинные многомерные массивы.
Вариант 1. Ступенчатый массив (Массив массивов)
Это подход, продемонстрированный в примере выше. Мы создаем одномерный массив, элементами которого вручную назначаем другие одномерные массивы. Главное преимущество такого метода — гибкость. "Строки" нашей таблицы могут иметь совершенно разную длину:
ЗубчатыйМассив = Новый Массив;
Строка1 = Новый Массив(2); // Длина - 2 элемента
Строка2 = Новый Массив(5); // Длина - 5 элементов
ЗубчатыйМассив.Добавить(Строка1);
ЗубчатыйМассив.Добавить(Строка2);
Вариант 2. Истинный многомерный массив
Если нам нужна классическая прямоугольная матрица с фиксированной размерностью, платформа предоставляет возможность инициализировать многомерный массив сразу через конструктор. Для этого достаточно перечислить размерности через запятую:
// Создаем прямоугольную матрицу: 3 строки и 5 колонок
ДвумерныйМассив = Новый Массив(3, 5);
При такой инициализации платформа автоматически заполнит все ячейки значением Неопределено. Обращение к элементам такой матрицы происходит с использованием последовательных квадратных скобок:
// Записываем значение в ячейку на пересечении 1-й строки и 5-й колонки (индексация с нуля)
ДвумерныйМассив[0][4] = "Целевое значение";
// Чтение значения из матрицы
ПолученноеЗначение = ДвумерныйМассив[0][4];
Оба этих варианта могут быть без ограничений помещены в СписокЗначений в качестве значения его элементов.
Платформа 1С позволяет конструировать невероятно сложные иерархические структуры. Нам никто не мешает создать коллекцию вида: "Массив Таблиц значений, в строках которых хранятся массивы структур". Это абсолютно легальный код с точки зрения компилятора 1С.
Давайте проанализируем пример создания такой комплексной структуры:
// Инициализируем корневой массив
КорневойМассив = Новый Массив;
// Создаем Таблицу значений
ТаблицаДанных = Новый ТаблицаЗначений;
ТаблицаДанных.Колонки.Добавить("Идентификатор", Новый ОписаниеТипов("Число"));
ТаблицаДанных.Колонки.Добавить("КоллекцияЭлементов", Новый ОписаниеТипов("Массив"));
// Заполняем строку таблицы
НоваяСтрока = ТаблицаДанных.Добавить();
НоваяСтрока.Идентификатор = 101;
// Создаем вложенный массив для строки таблицы
ВнутреннийМассив = Новый Массив;
ВнутреннийМассив.Добавить("Значение_А");
ВнутреннийМассив.Добавить("Значение_Б");
НоваяСтрока.КоллекцияЭлементов = ВнутреннийМассив;
// Помещаем таблицу значений внутрь корневого массива
КорневойМассив.Добавить(ТаблицаДанных);
Несмотря на то, что платформа позволяет собирать подобные конструкции, при их использовании на практике мы неизбежно столкнемся с архитектурными ограничениями клиент-серверного взаимодействия.
Главная опасность при работе со сложными вложенными коллекциями в 1С кроется в понятии мутабельности и доступности типов данных на разных контекстах выполнения. Разберем эту ситуацию подробнее.
Такие типы данных, как Массив, Структура, Соответствие и СписокЗначений, относятся к универсальным коллекциям общего назначения. Они доступны как на стороне сервера, так и на тонком или веб-клиенте. Вы можете свободно передавать массив, содержащий внутри себя другие массивы и структуры, из серверной функции на клиент.
Однако объект ТаблицаЗначений (а также ДеревоЗначений) является строго серверным типом данных. Тонкий клиент "ничего не знает" о существовании таблицы значений. Если вы попытаетесь передать на тонкий клиент массив, внутри которого находится хотя бы одна таблица значений, система завершит работу аварийно, выдав ошибку: "Ошибка передачи мутабельного значения".
Как решить эту проблему?
Если перед нами стоит задача передать табличные структуры на сторону клиента внутри общего массива, мы должны предварительно выполнить их конвертацию в массив структур. Каждая структура в таком массиве будет эмулировать строку таблицы значений. Давайте рассмотрим готовый шаблон функции для такой конвертации:
&НаСервере
Функция КонвертироватьТаблицуВМассивСтруктур(ТаблицаДанных)
Результат = Новый Массив;
// Получаем массив имен колонок для быстрой сборки структур
МассивКолонок = Новый Массив;
Для Каждого Колонка Из ТаблицаДанных.Колонки Цикл
МассивКолонок.Добавить(Колонка.Имя);
КонецЦикла;
// Переносим данные из таблицы значений в массив структур
Для Каждого СтрокаТЗ Из ТаблицаДанных Цикл
НоваяСтруктура = Новый Структура;
Для Каждого ИмяКолонки Из МассивКолонок Цикл
НоваяСтруктура.Вставить(ИмяКолонки, СтрокаТЗ[ИмяКолонки]);
КонецЦикла;
Результат.Добавить(НоваяСтруктура);
КонецЦикла;
Возврат Результат;
КонецФункции
Полученный в результате работы этой функции массив структур можно без каких-либо ограничений передавать на тонкий клиент и обходить в циклах на стороне интерфейса.
В современной разработке интеграционных решений крайне популярна передача данных посредством формата JSON. Платформа 1С предоставляет мощный встроенный инструмент — объект ЗаписьJSON. Он без проблем справляется с автоматической сериализацией примитивных типов данных, массивов, структур и соответствий.
Однако если в вашей вложенной структуре, которую вы планируете упаковать в JSON, окажется СписокЗначений или ТаблицаЗначений, метод ЗаписатьJSON() завершится ошибкой, поскольку данные типы не поддерживаются спецификацией JSON напрямую.
Чтобы обойти это ограничение, нам необходимо использовать специальный параметр — функцию преобразования. Давайте разберем по шагам, как настроить сериализацию "сложного" массива, содержащего неподдерживаемые типы данных:
&НаСервере
Процедура СериализоватьСложнуюКоллекциюВJSON()
// Подготовим данные со Списком значений внутри массива
СложнаяСтруктура = Новый Массив;
ВложенныйСписок = Новый СписокЗначений;
ВложенныйСписок.Добавить("Элемент 1");
ВложенныйСписок.Добавить("Элемент 2");
СложнаяСтруктура.Добавить(ВложенныйСписок);
// Настраиваем запись JSON
Запись = Новый ЗаписьJSON;
ПараметрыЗаписи = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Авто, " ");
Запись.УстановитьСтроку(ПараметрыЗаписи);
// Вызываем запись с указанием обработчика нестандартных типов
ЗаписатьJSON(Запись, СложнаяСтруктура, "МояФункцияПреобразования");
СтрокаРезультат = Запись.Закрыть();
Сообщить(СтрокаРезультат);
КонецПроцедуры
&НаСервере
Функция МояФункцияПреобразования(Свойство, Значение, ДополнительныеПараметры, Отказ) Экспорт
// Если обнаруживаем СписокЗначений, преобразуем его в простой массив значений
Если ТипЗнч(Значение) = Тип("СписокЗначений") Тогда
МассивИзСписка = Новый Массив;
Для Каждого ЭлементСписка Из Значение Цикл
МассивИзСписка.Добавить(ЭлементСписка.Значение);
КонецЦикла;
Возврат МассивИзСписка;
КонецЕсли;
// Если тип стандартный - оставляем без изменений
Возврат Значение;
КонецФункции
Использование функции преобразования позволяет гибко управлять выгрузкой любых нестандартных типов данных платформы 1С во внешние форматы обмена.
Бывают ситуации, когда многомерные массивы из 1С необходимо передать во внешние программные продукты через COM-технологию (например, при прямой выгрузке данных в листы Microsoft Excel или при вызове методов самописных библиотек, разработанных на C# или C++).
Напрямую передать объект типа Массив (и уж тем более массив массивов) во внешнюю среду не получится: сторонние системы не умеют работать с внутренними объектами типов 1С. Для решения этой проблемы в платформе предусмотрен специальный объект-обертка — COMSafeArray. Он преобразует коллекции 1С в совместимый формат SAFEARRAY, используемый в среде Windows.
Рассмотрим пример конструирования такого массива для передачи в COM-объект:
// Создаем двумерный безопасный массив строк: 2 строки и 3 колонки
// Тип данных VT_BSTR указывает на то, что массив будет хранить строки
ДвумерныйБезопасныйМассив = Новый COMSafeArray("VT_BSTR", 2, 3);
// Заполняем массив значениями
ДвумерныйБезопасныйМассив.SetValue(0, 0, "Строка1_Колонка1");
ДвумерныйБезопасныйМассив.SetValue(0, 1, "Строка1_Колонка2");
ДвумерныйБезопасныйМассив.SetValue(0, 2, "Строка1_Колонка3");
ДвумерныйБезопасныйМассив.SetValue(1, 0, "Строка2_Колонка1");
ДвумерныйБезопасныйМассив.SetValue(1, 1, "Строка2_Колонка2");
ДвумерныйБезопасныйМассив.SetValue(1, 2, "Строка2_Колонка3");
// Теперь этот объект можно передать как параметр в Excel или другую DLL
// Например: COMОбъектExcel.Range("A1:C2").Value = ДвумерныйБезопасныйМассив;
Использование COMSafeArray гарантирует корректную интерпретацию типов данных на стороне принимающего приложения и позволяет избежать труднодиагностируемых ошибок памяти при работе через COM-соединение.
Платформа 1С предоставляет разработчику колоссальную свободу при работе с вложенными коллекциями. Мы можем хранить двумерные массивы (как ступенчатые, так и истинные многомерные) в списках значений, структурах и соответствиях. Однако при проектировании сложных систем всегда помните о правилах передачи данных между сервером и клиентом, а также о необходимости явной конвертации специфических серверных объектов перед их передачей или сериализацией.