在C#中访问任意秩数组的索引

5
我需要迭代一个维度任意的数组。这个迭代包括读取和写入操作,所以GetEnumerator方法不能使用。

Array.SetValue(object, int)不能用于多维数组。 Array.SetValue(object, params int[])需要过多的算术计算来迭代多维空间。它还需要动态调用来避开签名中的params部分。

我想通过指针将数组固定并进行迭代,但是我找不到任何说明多维数组保证是连续的文档。如果它们在维度结尾处具有填充,则无法使用该方法。我也希望避免使用不安全的代码。

有没有一种简单的方法可以仅使用单个索引顺序地处理多维数组?


2
我认为使用“不安全(unsafe)”代码并没有错误。当你确实知道自己在做什么时,使用指针是安全的,并且是一种可行且高效的解决方案。 - Mikael Svenson
2
顺便说一句,将“int []”传递到期望“params int []”的位置是完全有效的。 - dtb
@dtb,你似乎在语法方面都是正确的。我从来不知道你可以直接将数组传递给params数组参数。 - Kennet Belenky
2
@Mikael Svenson,我同意不必害怕不安全的代码,但如果有等效的安全解决方案,我认为应该避免使用不安全的代码。 - Kennet Belenky
3个回答

5

多维数组保证是连续的。根据ECMA-335:

数组元素必须按行主序在数组对象中布局(即,与最右边的数组维度相关联的元素应从最低索引到最高索引连续布局)。

所以这样可以工作:

int[,,,] array = new int[10, 10, 10, 10];

fixed (int* ptr = array)
{
    ptr[10] = 42;
}

int result = array[0, 0, 1, 0];  // == 42

谢谢,这正是我在寻找的,并为我提供了一个很好的备用实现。顺便说一下,我认为你的代码会在“int* ptr = array”上出错,但这个想法是正确的。 - Kennet Belenky
@Kennet Belenky:我刚刚仔细检查了代码,它的运行非常完美。当然,如果你想避免使用不安全的代码,那么涉及到不安全代码的解决方案并没有帮助,但我担心没有其他解决方案可以避免大量(不必要的)开销(例如递归迭代所有维度)。 - dtb
然而,事实证明,只有当数组包含原始值类型时,固定才起作用。我在最初的问题中没有提到这一点,但我试图解决的数组还具有任意的ElementType,并且可能包含结构体、引用类型或装箱值类型。我想我必须采用使用索引数组的方法。 - Kennet Belenky

1

您可以使用 RankGetUpperBound 属性/方法创建一个索引数组,然后将其传递给数组的 SetValueGetValue 方法:

int[] Indices(Array a, int idx)
{
    var indices = new int[a.Rank];

    for (var i = 0; i < a.Rank; i++)
    {
        var div = 1;

        for (var j = i + 1; j < a.Rank; j++)
        {
            div *= a.GetLength(j);
        }

        indices[i] = a.GetLowerBound(i) + idx / div % a.GetLength(i);
    }

    return indices;
}

然后像这样使用:

for (var i = 0; i < array.Length; i++)
{
    var indices = Indices(array, i);
    array.SetValue(i, indices);
    var val = array.GetValue(indices);
}

1
是的,那就是我所说的过度算术。你还需要使用Array.GetLowerBound,而不仅仅是Array.GetUpperBound。 - Kennet Belenky
@kennet 实际上,我们不需要 Array.GetUpperBound,只需使用Array.GetLength方法 :) - Josef Pfleger

-1
也许你可以将它们全部合并到一个临时集合中,然后只需在该集合上进行迭代。

足够简单,我只需使用GetEnumerator来处理数组中的所有内容。问题是我还必须在数组的各个点上进行写入操作,而GetEnumerator将无法处理这种情况。 - Kennet Belenky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接