如何创建起始索引为 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的主要内容,如果未能解决你的问题,请参考以下文章

Pandas DataFrame 的起始索引为 1

Dlib 面部标志起始索引

如何知道无序数组的最后一个索引

在 Python pandas 中,从 1 而不是 0 开始行索引而不创建额外的列

iOS:将 UITableView 的起始索引从 0 改为 1 以显示数组内容

小试牛刀-1.7