Как создать ArrayList с начальным индексом 1 (вместо 0)

Как я могу запустить индекс в ArrayList в 1 вместо 0? Есть ли способ сделать это непосредственно в коде?

(Обратите внимание, что я прошу ArrayList, для простых массивов, пожалуйста, см. Инициализация массива на произвольном начальном индексе в С#)

Ответ 1

Очевидным способом сделать это было бы объединение ArrayList в вашу собственную реализацию и вычитание 1 из индексатора во все операции, в которых используется индекс.

Фактически вы также можете объявить массив в .NET, который имеет произвольную нижнюю границу (прокрутите вниз до раздела "Создание массивов с помощью ненулевая нижняя граница" ).

С учетом сказанного, пожалуйста, не делайте этого в производственном коде. Важно быть последовательным.

Ответ 2

Одной из законных оснований для этого является написание модульных тестов. Некоторые сторонние SDK (например, Excel COM interop) ожидают, что вы будете работать с массивами, которые имеют индексы на основе одного. Если вы работаете с таким SDK, вы можете и должны заглушить эту функциональность в своей тестовой среде С#.

Следующее должно работать:

object[,] comInteropArray = Array.CreateInstance(
    typeof(object),
    new int[] { 3, 5 },
    new int[] { 1, 1 }) as object[,];

System.Diagnostics.Debug.Assert(
     1 == comInteropArray.GetLowerBound(0),
    "Does it look like a COM interop array?");
System.Diagnostics.Debug.Assert(
     1 == comInteropArray.GetLowerBound(1),
    "Does it still look like a COM interop array?");

Я не знаю, можно ли использовать это как стандартный массив типов С#. Возможно нет. Я также не знаю, как сделать чистый код для начального значения такого массива, кроме цикла, чтобы скопировать их из стандартного массива С# с жестко заданными начальными значениями. Ни один из этих недостатков не должен иметь большого значения в тестовом коде.

Ответ 3

Ну, вы можете сделать свой собственный:

public class MyArrayList<T> extends ArrayList<T> {
    public T get(int index) {
        super.get(index - 1);
    }

    public void set(int index, T value) {
        super.set(index - 1, value);
    }
}

Но он задает вопрос: почему бы вам не беспокоиться?

Ответ 4

Я не знаю, будет ли это делать, но в какой-то момент Visual Basic позволит вам начинать индексы массивов в 1 с помощью Option Base 1.

В С# ни одна из коллекций System.Collections не поддерживает это, но вы всегда можете написать класс-оболочку, который выполняет перевод индекса для вас.

Действительно, этого следует избегать. VB Option Base создал больше проблем, чем решил, я полагаю, потому что он нарушил предположения и сделал вещи более запутанными.

Ответ 5

Stuff getValueAtOneBasedIndex(ArrayList<Stuff> list, index) {
     return list.get(index -1);
}

Ответ 6

Как показывают другие ответы, есть способы имитировать это, но нет прямого способа сделать это на С# или других широко используемых языках .NET. Цитирование Эрик Гуннерсон:

CLR поддерживает этот вид построить (мне было трудно не использовать термин "пародия" здесь...), но нет, насколько мне известно, никаких языки, которые имеют встроенный синтаксис для сделайте это.

Ответ 7

Вот что я сейчас использую, чтобы быстро перенести приложение VBA на С#:

Это класс Array, который начинается с 1 и принимает несколько измерений.

Хотя есть некоторая дополнительная работа (IEnumerator и т.д.), это начало, и этого достаточно для меня, поскольку я использую только индексы.

public class OneBasedArray<T>
{
    Array innerArray;

    public OneBasedArray(params int[] lengths)
    {
        innerArray = Array.CreateInstance(typeof(T), lengths, Enumerable.Repeat<int>(1, lengths.Length).ToArray());
    }

    public T this[params int[] i]
    {
        get
        {
            return (T)innerArray.GetValue(i);
        }
        set
        {
            innerArray.SetValue(value, i);
        }
    }
}

Ответ 8

Во-первых, позвольте мне предисловие к этому ответу, объяснив мой случай использования: Excel Interop. Гораздо проще читать и писать данные с помощью массивов, но Excel Range.Value2 магически возвращает массив объектов на основе 1.

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

object[,] newArray = new object[indexR, indexC];

Вы можете использовать:

object[,] newArray = (object[,])RangeToWriteTo.Value2;

В моем случае я создал класс-оболочку, чтобы позволить мне использовать массив для таких свойств:

public abstract class ExcelRowBase
    {
        public object[,] data;

        public ExcelRowBase(int index)
        {
            data = new object[2, index + 1];
        }
    }

 public class InstanceRowModel : ExcelRowBase 
    {
        public InstanceRowModel() : base(8) 
        { 
        //constructor unique to Wire Table 
        }

        public object Configuration
        {
            get
            {
                return data[1, 1];
            }
            set
            {
                data[1, 1] = value;
            }
        }
...

Поэтому во всех случаях я читаю один и тот же индекс. Итак, чтобы перейти от модели, я перехожу к методу Create, мне просто нужно скопировать свойства в модель, которая использует массив, созданный из Excel.

            insertingModel.data = (object[,])writeRange.Value2;

            //manually copying values from one array to the other
            insertingModel.Configuration = model.Configuration;
            ...

            writeRange.Value2 = insertingModel.data;

Теперь это работает для меня, потому что я должен сделать это только в функции create. В update/get/delete вы все равно получаете массив на основе Excel. Возможно, будущее улучшение будет заключаться в создании диапазона Excel factory, который избегает построенной по умолчанию конструкции по умолчанию, но это решение просто дало мне зелени на моих тестах, поэтому я продвигаюсь!