如何创建起始索引为 1(而不是 0)的 ArrayList
Posted
技术标签:
【中文标题】如何创建起始索引为 1(而不是 0)的 ArrayList【英文标题】:How can I create an ArrayList with a starting index of 1 (instead of 0) 【发布时间】:2011-02-17 10:49:17 【问题描述】:如何在ArrayList
中以 1 而不是 0 开始索引?有没有办法直接在代码中做到这一点?
(请注意,我要求的是ArrayList
,对于普通数组,请参阅Initializing an array on arbitrary starting index in c#)
【问题讨论】:
你为什么要这样做? @Eric 我会说为什么,在我们大学的第一年,学生们学习 Pascal,这里的索引从 1 开始,所以他们问他们是否可以用 c# 做同样的事情(只是一个问题) . 不使用ArrayList
怎么样?至少在 .NET 中,它从 5 年前就被通用集合所取代。
当你学习西班牙语时,你不会问“有没有办法说英语让他们听懂我的意思?”。计算机语言也是如此。当你“说”一种计算机语言时,你必须使用它的词汇。 C# 有以 0 开头的数组。如果要以 1 开头,请编写 Pascal。
我目前有一个业务需求,就是将工程师在 VBA Excel 中编写的计算代码移植到我们的 C# 自动化服务架构中。工程师对计算有很多了解,而对编程却知之甚少。他已经尽了最大的努力,并在他的线性方程算法中使用了数组。因为必须这样做,所以我没有时间浪费在重写所有数组逻辑上,而且我现在有一个很好的理由在 C# 中拥有以 1 开头的特殊数组。这可以干净地完成并有据可查。所以感谢这个问题。
【参考方案1】:
首先,让我先解释一下我的用例: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 函数中执行此操作。在更新/获取/删除中,无论如何您都会获得基于 Excel 的数组。也许未来的改进是创建一个 Excel 范围工厂,它完全避免了基于 0 的默认构造,但这个解决方案只是让我的测试变得绿色,所以我继续前进!
【讨论】:
【参考方案2】:这是我现在用来快速将 VBA 应用程序移植到 C# 的东西:
是一个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);
【讨论】:
这太棒了!我正在转换一些旧的 Spectrum Basic 游戏,这是非常宝贵的。【参考方案3】:这样做的一个正当理由是编写单元测试。某些第 3 方 SDK(例如 Excel COM 互操作)希望您使用具有基于 1 的索引的数组。如果您正在使用这样的 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# 数组复制它们。这些缺点在测试代码中都不是什么大问题。
【讨论】:
我注意到我的评论适用于数组,而不是 ArrayLists。哎呀。我希望使用 Excel 和 VTSO 的人仍然会发现它很有用,即使它偏离了最初的问题。【参考方案4】:我不知道它是否仍然如此,但在某一时刻,Visual Basic 会让您使用 Option Base 1
从 1 开始数组索引。
在 C# 中,没有一个 System.Collections 集合支持这一点,但您始终可以编写一个包装类来为您进行索引转换。
不过,确实应该避免这种情况。 VB 的Option Base
造成的问题比解决的问题多,我想是因为它打破了假设,让事情变得更加混乱。
【讨论】:
【参考方案5】:正如其他答案所暗示的,有一些方法可以模拟这一点,但没有直接的方法可以在 C# 或其他常用的 .NET 语言中执行此操作。引用Eric Gunnerson:
CLR 确实支持这种 构造(我很难不使用 这里的“讽刺”一词......),但是 据我所知,没有 具有内置语法的语言 这样做。
【讨论】:
【参考方案6】:嗯,你可以自己做:
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);
但它引出了一个问题:你到底为什么要打扰?
【讨论】:
【参考方案7】:Stuff getValueAtOneBasedIndex(ArrayList<Stuff> list, index)
return list.get(index -1);
【讨论】:
【参考方案8】:显而易见的方法是在您自己的实现中包装一个 ArrayList,并在所有使用索引的操作中从索引器中减去 1。
实际上,您也可以在 .NET 中声明一个数组,它有一个 arbitrary lower bound(向下滚动到“创建具有非零下限的数组”部分)。
话虽如此,请不要在生产代码中这样做。保持一致很重要。
【讨论】:
以上是关于如何创建起始索引为 1(而不是 0)的 ArrayList的主要内容,如果未能解决你的问题,请参考以下文章
在 Python pandas 中,从 1 而不是 0 开始行索引而不创建额外的列