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