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

时间:2010-05-09 20:37:52

标签: c# java arraylist

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

(请注意,我要求ArrayList,对于普通数组,请参阅Initializing an array on arbitrary starting index in c#

8 个答案:

答案 0 :(得分:19)

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

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

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

答案 1 :(得分:7)

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

以下内容应该有效:

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#数组中复制它们。在测试代​​码中,这些缺点都不应该是一个大问题。

答案 2 :(得分:4)

嗯,你可以自己做:

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);
    }
}

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

答案 3 :(得分:2)

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

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

但是,真的应该避免这种情况。 VB的Option Base创造的问题多于它解决的问题,我想是因为它破坏了假设并使事情更加混乱。

答案 4 :(得分:1)

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

答案 5 :(得分:1)

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

  

CLR确实支持这种方式   构造(我很难不使用   这里的“讽刺”一词......),但是   据我所知,没有   具有内置语法的语言   那样做。

答案 6 :(得分:1)

我现在正在使用这些东西快速将VBA应用程序移植到C#:

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

虽然还有一些额外的工作要做(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);
        }
    }
}

答案 7 :(得分:0)

首先,让我通过解释我的用例来解释这个答案: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;
            }
        }
...

所以在所有情况下,我都是从同一个索引中读取的。因此,为了从模型I转换到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函数中执行此操作。在更新/获取/删除中,您无论如何都要获得基于Excel的数组。也许未来的改进可能是创建一个Excel范围工厂,它可以避免基于0的默认构造,但是这个解决方案只是给了我测试的绿色,所以我继续前进!