Как передать и вывести массив в коде 1С

· 9 мин

Содержание

Массив в 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 По Массив.Количество() Цикл
    Индекс = Массив.Количество() - Сч;
    Если НужноУдалить(Массив[Индекс]) Тогда
        Массив.Удалить(Индекс);
    КонецЕсли;
КонецЦикла;

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

Почему изменения массива внутри процедуры видны снаружи?
Массив — ссылочный тип. Параметр получает ссылку на тот же объект, поэтому добавление и удаление элементов отражается у вызывающего кода.
Сохраняет ли Знач копию массива?
Нет. Знач лишь не даёт переприсваиванию параметра выйти наружу. Содержимое остаётся общим — чтобы изолировать массив, сделайте копию перебором или через сериализацию.
Можно ли передать массив с клиента на сервер параметром?
Да, если он сериализуемый — массив из примитивов и ссылок проходит. Для больших объёмов надёжнее временное хранилище.
Как сделать глубокую копию массива со вложенными структурами?
Запишите массив через ЗначениеВСтрокуВнутр и прочитайте обратно ЗначениеИзСтрокиВнутр — получите независимую копию вместе с вложенными объектами.
Как вывести массив в сообщениях пользователю?
Переберите его в цикле и выводите каждый элемент через Сообщить, либо соберите массив строк в одну строку функцией СтрСоединить.