【问题标题】:How can I create an ArrayList with a starting index of 1 (instead of 0)如何创建起始索引为 1(而不是 0)的 ArrayList
【发布时间】: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 开头的特殊数组。这可以干净地完成并有据可查。所以感谢这个问题。

标签: c# java arraylist


【解决方案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(向下滚动到“创建具有非零下限的数组”部分)。

            话虽如此,请不要在生产代码中这样做。保持一致很重要。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2023-03-30
              • 2015-11-21
              • 2020-05-18
              • 2021-05-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多