c#中使用传统[]数组语法的非0数组

本文关键字:数组 语法 传统 | 更新日期: 2023-09-27 18:02:13

我希望能够在c#中创建一个索引范围任意范围的数组,例如,一个索引为100-115的16个元素数组。

c#中的原生数组是基于0的,但我被告知(例如,在Luaan的评论中),c# Array类允许任意的下界和上界。但是在示例中,我看到Array类中的元素是通过myArray.GetValue()和myArray.SetValue()来访问的,而不是像myArray.[ foo ]. 这样的传统数组语法。
Array arr = Array.CreateInstance(typeof(string), new[]{16}, new[]{100});
Console.WriteLine(arr.Length); // 16
arr.SetValue("foo", 100);
Console.WriteLine(arr.GetValue(100)); // foo

是否有任何方法可以使数组具有一些任意起始索引,如[100],我可以使用c#中的传统[]语法访问?

c#中使用传统[]数组语法的非0数组

你可以创建一个实现装饰器模式的类:只需实现IList接口(也由Array实现),并对this [int index]属性进行任何移动。

装饰器模式描述如下:http://www.codeproject.com/Articles/479635/UnderstandingplusandplusImplementingplusDecoratorp

Array类不支持此操作,但您可以编写自己的索引为1的数组类:

public class OneBasedArray<T>
{
    public T[] InnerArray;
    public T this[int i]
    {
        get { return InnerArray[i-1]; }
        set { InnerArray[i-1] = value; }
    }
}

然后像这样使用:

var myArray = new OneBasedArray<int> { InnerArray = new int[]{ 1, 2, 3, 4, 5 } };
for(int i = 1; i <=5; i++)
{
    Console.WriteLine(myArray[i]);
}

这段代码只是为了得到一个想法,这样的类当然需要一个更好的接口。

只有当数组的索引为0时,才能使用数组的索引器

您可以为自定义的非数组类型使用索引器,并使用您想要的任何逻辑,例如使其非零索引,但这不是数组的选项。

我认为Lukas的答案可能是处理这个问题最简单的方法,但是如果,出于某种原因,您真的想使用Array.CreateInstance(不确定为什么要这样做-也许某些外部库可能会坚持使用它?),您可以将它包装在这样的类中:

public class NonZeroArray<T>
{
    private readonly Array array;
    public T this[int i]
    {
        get { return (T)array.GetValue(i); }
        set { array.SetValue(value, i); }
    }
    public NonZeroArray(int length, int lowerBounds = 0)
    {
        array = Array.CreateInstance(typeof(T), new int[] { length}, new int[] { lowerBounds } );
    }
}

显然,你可以让它实现IList<T>的其余部分,使它更漂亮(更容易使用)。如果你真的需要原生数组,你可以实现一个带有getter的属性,以便在需要的时候公开它。

使用它很简单:

var myNonZeroArray = new NonZeroArray<string>(16,100);
myNonZeroArray[100] = "foo";
Console.WriteLine(myNonZeroArray[100]);   // prints "foo"

这是一个扩展IList<T>的自定义类,以提供从非零索引开始的功能:

public class NonZeroList<T> : IList<T>
{
    private int startIndex;
    private List<T> inner;
    public NonZeroList(int startIndex, IEnumerable<T> content)
    {
        this.startIndex = startIndex;
        inner = content.ToList();
    }
    public NonZeroList(int startIndex)
    {
        this.startIndex = startIndex;
        inner = new List<T>();
    }
    public T this[int i]
    {
        get
        {
            return inner[i - startIndex];
        }
        set
        {
            inner[i - startIndex] = value;
        }
    }
    public IEnumerator<T> GetEnumerator()
    {
        foreach (T i in inner)
            yield return i;
        yield break;
    }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return inner.GetEnumerator();
    }
    public int IndexOf(T item)
    {
        return inner.IndexOf(item) + startIndex;
    }
    public void Insert(int index, T item)
    {
        inner.Insert(index - startIndex, item);
    }
    public void RemoveAt(int index)
    {
        inner.RemoveAt(index - startIndex);
    }
    public void Add(T item)
    {
        inner.Add(item);
    }
    public void Clear()
    {
        inner.Clear();
    }
    public bool Contains(T item)
    {
        return inner.Contains(item);
    }
    public void CopyTo(T[] array, int arrayIndex)
    {
        inner.CopyTo(array, arrayIndex);
    }
    public int Count
    {
        get { return inner.Count; }
    }
    public bool IsReadOnly
    {
        get { return false; }
    }
    public bool Remove(T item)
    {
        return inner.Remove(item);
    }
}

要使用它,需要初始化它,就像初始化普通的List<T>一样,但是要指定起始索引(例如NonZeroList<int> myList = new NonZeroList<int>(20) { 0, 1, 2, 3 };)。然后,您可以像使用普通List<T>T[]一样使用它。

如果您想将其用作数组而不是列表,那么您可以简单地添加边界检查或实现IEnumerable<T>而不是IList<T>,并自己创建实用程序函数。