如何在ArrayList
中以1而不是0开始索引?有没有办法直接在代码中执行此操作?
(请注意,我要求ArrayList
,对于普通数组,请参阅Initializing an array on arbitrary starting index in c#)
答案 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的默认构造,但是这个解决方案只是给了我测试的绿色,所以我继续前进!