如何创建一个起始索引为1(而不是0)的ArrayList

如何在ArrayList以1而不是0启动索引? 有没有办法直接在代码中执行此操作?

(注意我要求ArrayList ,对于普通数组,请参阅在c#中的任意起始索引上初始化数组 )

显而易见的方法是在您自己的实现中包装ArrayList,并在使用索引的所有操作中从索引器中减去1。

实际上,您也可以在.NET中声明一个具有任意下限的数组(向下滚动到“创建具有非零下限的数组”部分)。

话虽如此,请不要在生产代码中执行此操作。 重要的是保持一致。

这样做的一个合理理由是编写unit testing。 某些第三方SDK(例如Excel COM interop)希望您使用具有基于一个索引的数组。 如果您正在使用这样的SDK,您可以并且应该在C#测试框架中删除此function。

以下应该有效:

 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?"); 

我不知道是否可以将其转换为标准的C#样式数组。 可能不是。 除了循环从具有硬编码初始值的标准C#数组中复制它们之外,我还不知道对这种数组的初始值进行硬编码的简洁方法。 在测试代​​码中,这些缺点都不应该是一个大问题。

好吧,你可以做自己的:

 public class MyArrayList extends ArrayList { public T get(int index) { super.get(index - 1); } public void set(int index, T value) { super.set(index - 1, value); } } 

但这引出了一个问题:你为什么要打扰?

我不知道它是否仍然存在,但是在某一点上,Visual Basic会让你使用Option Base 1在1处启动数组索引。

在C#中,没有System.Collections集合支持这一点,但您总是可以编写一个包装类来为您执行索引转换。

但实际上,应该避免这种情况。 VB的Option Base创造了比它解决的问题更多的问题,我想是因为它打破了假设并使事情变得更加混乱。

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

正如其他答案所示,有一些方法可以模拟这种情况,但没有直接的方法可以在C#或其他常用的.NET语言中执行此操作。 引用Eric Gunnerson :

CLR确实支持这种构造(我很难在这里使用术语“travesty”……),但据我所知,还没有任何具有内置语法的语言。

以下是我现在正在使用的快速将VBA应用程序移植到C#的内容:

它是一个以1开头并接受多个维度的Array类。

虽然还有一些额外的工作要做(IEnumerator等),但这是一个开始,对我来说已经足够了,因为我只使用索引器。

 public class OneBasedArray { Array innerArray; public OneBasedArray(params int[] lengths) { innerArray = Array.CreateInstance(typeof(T), lengths, Enumerable.Repeat(1, lengths.Length).ToArray()); } public T this[params int[] i] { get { return (T)innerArray.GetValue(i); } set { innerArray.SetValue(value, i); } } } 

首先,让我通过解释我的用例来解释这个答案:Excel Interop。 使用数组读取和写入数据要容易得多,但Excel Range.Value2会奇怪地返回一个基于1的对象数组。

所以,如果你正在写Excel,这就是你首先提出这个问题的原因……那么也许就是停止对抗c#并让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范围工厂,它避免了基于0的默认构造,但是这个解决方案只是给了我测试的绿色,所以我继续前进!

Interesting Posts