Как передать и вывести массив в коде 1С
Содержание
Массив в 1С передаётся по ссылке: если вы отдали его в процедуру и там добавили элемент, снаружи массив тоже изменится. Это сбивает с толку тех, кто ждёт, что параметр будет копией. Разберём, как сообщить массив в окно сообщений, как корректно передавать его между процедурами, во внешние обработки и на сервер, и чем массив отличается от таблицы значений.
Главный источник ошибок здесь — путаница между «передать ссылку на тот же объект» и «передать копию». Второй частый вопрос — как вытащить массив из клиентского контекста на сервер, когда данных много. Оба случая закрываются конкретными приёмами платформы.
Что значит вывести и передать массив
Слово «сообщить» применительно к массиву читают двумя способами. Первый — буквально вывести содержимое через Сообщить(). Функция принимает строку, поэтому массив целиком ей не отдашь — его перебирают:
Для Каждого Элемент Из Массив Цикл
Сообщить(Элемент);
КонецЦикла;
В управляемом приложении Сообщить считается устаревшим. Для сообщений, привязанных к полю формы, используйте объект СообщениеПользователю с методом Сообщить(). Для простого вывода в журнал при отладке Сообщить() по-прежнему удобен.
Если все элементы — строки, короче собрать их в одну строку:
Сообщить(СтрСоединить(Массив, ", "));
СтрСоединить ждёт массив строк. Числа и даты заранее приводите к строке через Формат() или Строка(), иначе получите ошибку преобразования типа:
Строки = Новый Массив;
Для Каждого Элемент Из Массив Цикл
Строки.Добавить(Строка(Элемент));
КонецЦикла;
Сообщить(СтрСоединить(Строки, "; "));
Второй смысл — передать массив как данные: в другую процедуру, во внешнюю обработку , с клиента на сервер или в другую базу. Это основная тема статьи, и здесь важно понимать природу типа.
Массив как ссылочный тип: передача по ссылке и Знач
В языке 1С типы делятся на две группы. Значимые типы (Число, Строка, Дата, Булево) при присваивании копируются. Ссылочные (объектные) типы — Массив, Структура, Соответствие, СписокЗначений, ТаблицаЗначений — хранятся как объект, а переменная держит ссылку на него.
Для числа это очевидно: передали в процедуру, изменили внутри — снаружи осталось прежним, потому что процедура работала с копией значения. С массивом интуиция подводит ровно потому, что копии не возникает.
Отсюда поведение при передаче в процедуру. Параметр получает ту же ссылку, поэтому изменения содержимого видны вызывающему коду:
Процедура ДобавитьЭлемент(Список)
Список.Добавить("Второй");
КонецПроцедуры
Массив = Новый Массив;
Массив.Добавить("Первый");
ДобавитьЭлемент(Массив);
Сообщить(Массив.Количество()); // 2
Теперь про Знач. Многие думают, что Знач делает копию массива. Это не так.
Ключевое слово Знач запрещает только одно: присвоить параметру новое значение так, чтобы это увидел вызывающий код. Содержимое массива по-прежнему общее — добавление и удаление элементов внутри процедуры видно снаружи и с Знач.
Разница проявляется при переприсваивании:
Процедура Пересоздать(Знач Список)
Список = Новый Массив; // снаружи НЕ видно
Список.Добавить("Новый");
КонецПроцедуры
Процедура Дополнить(Список)
Список = Новый Массив; // снаружи видно: массив заменён
КонецПроцедуры
Чтобы процедура работала с независимой копией и не трогала исходный массив, копию делают явно:
Копия = Новый Массив;
Для Каждого Элемент Из Исходный Цикл
Копия.Добавить(Элемент);
КонецЦикла;
Это поверхностная копия: если внутри лежат вложенные массивы или структуры, они останутся общими. Для глубокой копии используют сериализацию (см. раздел ниже) — записать значение в строку и прочитать обратно.
💡 Вернуть массив из функции можно обычным Возврат Массив. Возвращается та же ссылка — лишнего копирования не происходит.
Как передать массив во внешнюю обработку
Внешняя обработка — отдельный файл, подключённый к базе. Передать в неё массив параметром при подключении нельзя, поэтому используют два приёма.
Через экспортную процедуру модуля объекта обработки. В модуле обработки объявляете процедуру с ключевым словом Экспорт, принимающую массив:
// в модуле объекта внешней обработки
Процедура ПринятьДанные(Список) Экспорт
// работа с массивом
КонецПроцедуры
Вызывающий код подключает обработку и вызывает процедуру:
ИмяОбработки = ВнешниеОбработки.Подключить(АдресХранилища);
Обработка = ВнешниеОбработки.Создать(ИмяОбработки);
Обработка.ПринятьДанные(Массив);
Второй способ — через параметры открываемой формы. Форме обработки передают структуру параметров при ОткрытьФорму, а массив кладут внутрь этой структуры. В обработчике ПриСозданииНаСервере формы читаете Параметры.ИмяПараметра.
Для дополнительных обработок, подключённых через подсистему Библиотеки стандартных подсистем, набор данных команде передаётся по правилам этой подсистемы — через структуру параметров выполнения. Если точный состав полей не помните, смотрите описание команды в модуле обработки.
Результат удобно возвращать тем же приёмом «по ссылке». Если экспортной процедуре передать массив, который она должна заполнить, обработка складывает в него результат, а вызывающий код читает заполненный массив после вызова — отдельный механизм возврата не нужен:
// в модуле объекта обработки
Процедура ОтобратьПодходящие(Исходные, Результат) Экспорт
Для Каждого Элемент Из Исходные Цикл
Если ЭтоПодходит(Элемент) Тогда
Результат.Добавить(Элемент);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Временное хранилище для больших массивов и обмена клиент-сервер
Когда массив большой или его надо переправить между клиентом и сервером, прямая передача параметром неудобна и ограничена сериализуемостью. Для этого есть временное хранилище.
Логика такая: на сервере кладёте значение в хранилище и получаете строку-адрес, адрес отдаёте на клиент, по адресу забираете данные обратно.
&НаСервере
Функция СохранитьМассив(Данные)
Возврат ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор);
КонецФункции
// получить обратно — на клиенте или на сервере
Данные = ПолучитьИзВременногоХранилища(Адрес);
Второй параметр ПоместитьВоВременноеХранилище управляет временем жизни данных:
- 🔑 передали
УникальныйИдентификаторформы — данные живут, пока открыта форма; - 🔑 передали новый
Новый УникальныйИдентификатор— вернётся его адрес, удалять придётся вручнуюУдалитьИзВременногоХранилища; - 🔑 не передали ничего — данные живут до конца серверного вызова.
Через хранилище удобно гонять не только массивы, но и двоичные данные (файлы, картинки). Значение должно быть сериализуемым — массив из примитивов подходит, массив с объектами вроде ДокументОбъект — нет.
То же ограничение действует при передаче массива в фоновое (длительное) задание. Параметры процедуры фона собирают в структуру и передают заданию — и каждый из них, включая массив, обязан быть сериализуемым. Несериализуемое значение приведёт к ошибке запуска задания, поэтому в фон передают ссылки или примитивы, а тяжёлые объекты читают уже внутри фоновой процедуры по этим ссылкам.
Если ПолучитьИзВременногоХранилища вернул Неопределено, чаще всего адрес уже недействителен: форма, к которой привязаны данные, закрыта, либо истёк серверный вызов. Проверяйте результат перед использованием.
Массив или таблица значений: что выбрать
Массив — линейный список произвольных значений. Таблица значений — набор строк с именованными колонками. Выбор зависит от того, что за данные.
| Критерий | Массив | Таблица значений |
|---|---|---|
| Структура | плоский список | строки и колонки |
| Доступ к элементу | по индексу Массив[0] | по строке и имени колонки |
| Поиск | Массив.Найти(Значение) → индекс или Неопределено | Найти, НайтиСтроки(Отбор) |
| Группировка, итоги | нет | Свернуть, Итог |
| Копирование | вручную перебором | метод Скопировать() |
| Когда брать | простой список однотипных значений | записи с несколькими полями |
Практическое правило: нужен просто перечень (список ссылок, набор строк, идентификаторы) — берите массив. Нужны строки с полями и агрегацией — таблица значений.
Оба типа сериализуемы и спокойно проходят через временное хранилище. Разница в удобстве: таблицу значений не обязательно перебирать вручную — у неё есть сортировка, отбор и свёртка «из коробки».
Конвертировать одно в другое несложно. Выгрузить колонку таблицы в массив — одной строкой:
МассивЗначений = Таблица.ВыгрузитьКолонку("Контрагент");
Обратно — массив в таблицу — заполняют перебором, добавляя строки:
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Значение");
Для Каждого Элемент Из Массив Цикл
Строка = Таблица.Добавить();
Строка.Значение = Элемент;
КонецЦикла;
Какой приём передачи выбрать под задачу:
Сериализация массива: строка, XML и JSON
Сериализация нужна, когда массив надо сохранить в реквизит, записать в файл, передать во внешнюю систему или сделать глубокую копию.
Самый быстрый способ для своей базы — внутренний формат платформы:
СтрокаДанных = ЗначениеВСтрокуВнутр(Массив);
// ... сохранили или передали строку ...
Массив = ЗначениеИзСтрокиВнутр(СтрокаДанных);
ЗначениеВСтрокуВнутр работает с сериализуемыми значениями, включая массив из примитивов и ссылок. Формат непубличный и может меняться между версиями платформы — для долгого хранения и обмена с чужими системами он не годится, только внутри одной базы.
Для читаемого обмена между базами берут XDTO-сериализатор и XML:
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку();
СериализаторXDTO.ЗаписатьXML(Запись, Массив);
XMLСтрока = Запись.Закрыть();
Обратное чтение собирает массив из строки XML:
Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(XMLСтрока);
Массив = СериализаторXDTO.ПрочитатьXML(Чтение, Тип("Массив"));
Чтение.Закрыть();
XML предсказуем и переносим между базами и системами, но объёмнее внутреннего формата. Для разовой передачи внутри одной базы это избыточно — там быстрее ЗначениеВСтрокуВнутр.
Если принимающая сторона ждёт JSON, в платформе есть объекты ЗаписьJSON и ЧтениеJSON. Массив из примитивов ложится в JSON-массив напрямую. Для сложных значений, которые JSON не описывает, комбинируют запись JSON с XDTO-сериализатором.
Сериализация — это и есть надёжный способ глубокой копии: записали массив в строку и прочитали обратно — получили полностью независимую структуру вместе со вложенными объектами.
Отладка и типичные ошибки передачи
Когда массив «теряется» или приходит не тем, помогает проверка типа и содержимого в отладчике. В точке останова смотрите массив в окне «Табло» или через «Вычислить выражение», проверяйте Массив.Количество() и тип элементов.
Перед работой с полученным значением проверяйте его тип и не считайте, что массив всегда непустой:
Если ТипЗнч(Данные) = Тип("Массив") И Данные.Количество() > 0 Тогда
// обрабатываем
КонецЕсли;
Пустой массив — нормальное значение, а не ошибка: проверяйте Количество() перед обращением по индексу, а не ловите исключение постфактум.
Частые ошибки:
- ❌ Выход за границы — обращение
Массив[i], когдаiбольше или равенКоличество(). Платформа выдаёт «Индекс находится за границами массива». Индексация с нуля, последний элемент —Количество() - 1. - ❌ Удаление в прямом цикле — при удалении элементов индексы сдвигаются, и часть элементов пропускается. Идите обратным циклом от последнего индекса к первому.
- ❌ Несериализуемое значение — попытка передать на сервер или в хранилище массив с объектом, который не сериализуется (например, объект документа). Передавайте ссылки или примитивы, объект читайте уже на сервере.
- ❌ Неопределено из хранилища — адрес устарел, данные удалены вместе с формой. Кладите данные с идентификатором формы и проверяйте результат получения.
- ❌ Неожиданное изменение исходного массива — забыли, что передали ссылку, а не копию. Если менять исходный нельзя — копируйте явно.
Обратный цикл для безопасного удаления:
Для Сч = 1 По Массив.Количество() Цикл
Индекс = Массив.Количество() - Сч;
Если НужноУдалить(Массив[Индекс]) Тогда
Массив.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
Частые вопросы
Почему изменения массива внутри процедуры видны снаружи?
Сохраняет ли Знач копию массива?
Знач лишь не даёт переприсваиванию параметра выйти наружу. Содержимое остаётся общим — чтобы изолировать массив, сделайте копию перебором или через сериализацию.Можно ли передать массив с клиента на сервер параметром?
Как сделать глубокую копию массива со вложенными структурами?
ЗначениеВСтрокуВнутр и прочитайте обратно ЗначениеИзСтрокиВнутр — получите независимую копию вместе с вложенными объектами.Как вывести массив в сообщениях пользователю?
Сообщить, либо соберите массив строк в одну строку функцией СтрСоединить.