Как избежать ошибки «Индекс находится за границами массива» при работе с текущей строкой табличной части в 1С:Предприятии?

Программист 1С v8.3 (Управляемые формы) 1С:Розница Торговля и дистрибуция
← К списку

Приветствуем вас! Сегодня мы с вами разберем одну из частых и порой обескураживающих ошибок в 1С:Предприятии — «Индекс находится за границами массива». Эта ошибка возникает, когда программа пытается обратиться к элементу коллекции (массива, табличной части, списка) по индексу, который не существует, то есть выходит за пределы допустимого диапазона (обычно от 0 до количества элементов минус 1). В контексте работы с управляемыми формами и табличными частями, как показала практика на форумах 1С, эта проблема очень часто связана с неправильным пониманием того, что на самом деле возвращает свойство ТекущаяСтрока элемента формы. Давайте выясним причину и найдем правильные решения.

Разбираемся в корне проблемы: `ТекущаяСтрока` – это не индекс!

Первым делом, давайте проясним ключевое заблуждение, которое приводит к ошибке «Индекс находится за границами массива» при работе с табличными частями на управляемых формах. Мы часто интуитивно ожидаем, что свойство Элементы.ИмяТаблицы.ТекущаяСтрока или ЭтаФорма.ТекущийЭлемент.ТекущаяСтрока вернет нам порядковый номер (индекс) выбранной строки в табличной части, как это было в обычных формах или при работе с простыми массивами. Однако это не так. На самом деле, свойство ТекущаяСтрока возвращает не числовой индекс, а уникальный идентификатор строки табличной части формы. Давайте рассмотрим подробнее, что это означает:

  1. Что такое идентификатор строки? Это внутренний нумератор, который управляемые формы используют для однозначной идентификации каждой строки в табличном поле. Каждой новой строке присваивается уникальное значение.
  2. Чем идентификатор отличается от индекса?
    • Индекс — это порядковый номер элемента в коллекции, начинающийся с 0. Если мы удаляем элемент, индексы остальных элементов смещаются.
    • Идентификатор — это постоянное, уникальное значение, присвоенное строке при ее создании. Если вы удаляете строку, а затем добавляете новую, новой строке будет присвоен новый, уникальный идентификатор, который, скорее всего, будет отличаться от идентификатора ранее удаленной строки, даже если она займет то же "визуальное" место в таблице. Из-за этого в последовательности идентификаторов могут возникать пропуски. Нумератор идентификаторов сбрасывается только после закрытия и повторного открытия формы.
  3. Почему это важно? Если вы попытаетесь использовать это значение (идентификатор) как обычный числовой индекс для обращения к коллекции (например, Объект.Товары[ТекСтрокаИдентификатор]), скорее всего, вы получите ошибку «Индекс находится за границами массива», потому что идентификатор не является порядковым номером и может быть гораздо больше фактического количества строк или иметь произвольное значение.

Решение 1: Правильное использование идентификатора строки с методом `НайтиПоИдентификатору()`

Итак, если свойство ТекущаяСтрока возвращает идентификатор, то как же нам получить саму строку данных? Для этого в 1С:Предприятии предусмотрен специальный метод НайтиПоИдентификатору(), который применяется к коллекции данных (например, к табличной части объекта). Давайте разберем по шагам, как это работает:

  1. Получаем идентификатор текущей строки. Мы используем свойство ТекущаяСтрока элемента формы.
    
    ТекСтрокаИдентификатор = Элементы.Товары.ТекущаяСтрока;
    
    Здесь Элементы.Товары — это табличное поле на форме, а Товары — его источник данных.
  2. Используем метод `НайтиПоИдентификатору()`. Передаем полученный идентификатор в этот метод, примененный к коллекции данных.
    
    ТекущиеДанные = Объект.Товары.НайтиПоИдентификатору(ТекСтрокаИдентификатор);
    
    Здесь Объект.Товары — это табличная часть объекта, данные которой отображаются в табличном поле.
  3. Работаем с полученной строкой. После выполнения этих двух шагов, переменная ТекущиеДанные будет содержать объект строки табличной части, и вы сможете безопасно обращаться к ее реквизитам.
    
    ТекущиеДанные.Продавец = СтрокаРезультата.ВладелецКарты;
    // Или, например:
    // Сообщить("Выбран товар: " + ТекущиеДанные.Номенклатура);
    

Этот подход является наиболее корректным при работе с идентификаторами строк и позволяет избежать ошибки «Индекс находится за границами массива».

Решение 2: Прямой доступ к данным текущей строки через `ТекущиеДанные`

Помимо использования идентификатора, существует более прямой и часто более удобный способ получить данные текущей строки табличного поля на управляемой форме — это свойство ТекущиеДанные. Давайте посмотрим, как это работает:

  1. Используем свойство `ТекущиеДанные`. Свойство Элементы.ИмяТаблицы.ТекущиеДанные напрямую содержит объект с данными текущей строки табличного поля.
    
    ТекущиеДанныеСтроки = Элементы.Товары.ТекущиеДанные;
    
    Если строка не выбрана или таблица пуста, это свойство может вернуть Неопределено. Поэтому важно всегда проверять его на заполненность перед использованием.
  2. Работаем с данными. После получения объекта, вы можете сразу обращаться к его реквизитам.
    
    Если ТекущиеДанныеСтроки <> Неопределено Тогда
        ТекущиеДанныеСтроки.Продавец = СтрокаРезультата.ВладелецКарты;
        // Или:
        // Сообщить("Количество: " + ТекущиеДанныеСтроки.Количество);
    КонецЕсли;
    

Преимущества `ТекущиеДанные`: * Простота: Не нужно выполнять дополнительные вызовы методов, таких как НайтиПоИдентификатору(). * Производительность: Особенно на клиенте, это может быть быстрее, так как данные уже считаны и доступны. * Удобство: Более интуитивный способ получить данные текущей строки. Мы рекомендуем использовать свойство ТекущиеДанные для большинства случаев, когда вам нужен прямой доступ к реквизитам текущей строки табличного поля на управляемой форме.

Когда все-таки нужен индекс: Получение числового индекса строки

Иногда, несмотря на все вышесказанное, нам все-таки может понадобиться именно числовой индекс строки в коллекции (например, для работы с таблицами значений, или при специфических манипуляциях с массивами). В таких случаях мы можем получить индекс с помощью метода Индекс(). Давайте проанализируем ситуацию:

  1. Получаем объект текущей строки. Сначала нам нужно получить саму строку данных, например, с помощью ТекущиеДанные.
    
    ТекущаяСтрокаДанных = Элементы.Товары.ТекущиеДанные;
    
  2. Используем метод `Индекс()`. Затем мы можем вызвать метод Индекс() для коллекции, передав ему полученный объект строки.
    
    Если ТекущаяСтрокаДанных <> Неопределено Тогда
        ИндексСтроки = Объект.Товары.Индекс(ТекущаяСтрокаДанных);
        Сообщить("Индекс выбранной строки: " + ИндексСтроки);
    КонецЕсли;
    
    Здесь Объект.Товары — это коллекция, в которой мы ищем индекс.

Полученный ИндексСтроки будет числовым значением, начинающимся с 0. Теперь вы сможете использовать его для доступа к элементам коллекции по индексу, например:


Если ИндексСтроки <> Неопределено Тогда
    // Обращение к строке по индексу, например, в таблице значений
    СтрокаПоИндексу = МояТаблицаЗначений[ИндексСтроки];
КонецЕсли;

Помните, что метод Индекс() может быть не самым производительным решением для больших коллекций, так как он ищет переданный объект в коллекции. Используйте его, когда это действительно необходимо.

Общие рекомендации по предотвращению ошибки «Индекс находится за границами массива»

Хотя мы подробно разобрали случай с ТекущаяСтрока, ошибка «Индекс находится за границами массива» может возникать и по другим причинам. Давайте рассмотрим общие рекомендации, которые помогут вам избежать этой проблемы в различных сценариях:

  1. Всегда проверяйте границы перед обращением по индексу. Прежде чем обратиться к элементу коллекции по индексу, всегда убедитесь, что этот индекс находится в допустимом диапазоне (от 0 до КоличествоЭлементов - 1).
    
    МойМассив = Новый Массив();
    МойМассив.Добавить("Элемент 0");
    МойМассив.Добавить("Элемент 1");
    
    ИндексДляДоступа = 2; // Этот индекс выходит за границы массива
    
    Если ИндексДляДоступа >= 0 И ИндексДляДоступа < МойМассив.Количество() Тогда
        // Доступ безопасен
        Значение = МойМассив[ИндексДляДоступа];
    Иначе
        Сообщить("Попытка доступа по недопустимому индексу: " + ИндексДляДоступа);
    КонецЕсли;
    
  2. Аккуратно работайте с циклами. Если вы обходите коллекцию в цикле и удаляете из нее элементы, индексы оставшихся элементов смещаются. Чтобы избежать проблем, рекомендуется обходить коллекцию с конца к началу при удалении элементов.
    
    // Неправильно (может пропустить элементы или вызвать ошибку)
    // Для Каждого Элемент Из МояКоллекция Цикл
    //     Если НужноУдалить(Элемент) Тогда
    //         МояКоллекция.Удалить(Элемент);
    //     КонецЕсли;
    // КонецЦикла;
    
    // Правильно (обход с конца)
    Для Индекс = МояКоллекция.Количество() - 1 По 0 СШагом -1 Цикл
        Если НужноУдалить(МояКоллекция[Индекс]) Тогда
            МояКоллекция.Удалить(Индекс);
        КонецЕсли;
    КонецЦикла;
    
  3. Проверяйте коллекции на пустоту. Перед попыткой получить первый или единственный элемент коллекции, убедитесь, что она не пуста.
    
    Если МояКоллекция.Количество() > 0 Тогда
        ПервыйЭлемент = МояКоллекция[0];
    Иначе
        Сообщить("Коллекция пуста, невозможно получить первый элемент.");
    КонецЕсли;
    
  4. Тщательное тестирование. Всегда тестируйте ваш код в различных сценариях, особенно те участки, которые работают с коллекциями и индексами. Используйте отладчик, чтобы пошагово просмотреть выполнение кода и убедиться, что индексы и количество элементов соответствуют вашим ожиданиям.

Мы надеемся, что это подробное руководство поможет вам понять причины возникновения ошибки «Индекс находится за границами массива» при работе с текущей строкой табличной части в 1С:Предприятии и эффективно ее предотвращать. Используйте правильные методы и всегда помните о проверке границ, чтобы ваш код был надежным и стабильным!

← К списку