Как создать 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, который избегает построенной по умолчанию конструкции по умолчанию, но это решение просто дало мне зелени на моих тестах, поэтому я продвигаюсь!