【问题标题】:Indexing into arrays of arbitrary rank in C#在 C# 中索引到任意等级的数组
【发布时间】:2011-03-25 10:22:28
【问题描述】:

我需要遍历任意等级的数组。这是用于读写的,所以GetEnumerator 将不起作用。

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

我很想固定数组并用指针对其进行迭代,但我找不到任何说明多维数组保证是连续的文档。如果他们在维度的末尾有填充,那么这将不起作用。我也希望避免使用不安全的代码。

是否有一种简单的方法可以仅使用单个索引来顺序寻址多维数组?

【问题讨论】:

  • 我没有看到使用“不安全”代码的错误。当您真正知道自己在做什么时使用指针是安全的,而且它是一种可行且有效的解决方案。
  • 顺便说一句,在预期 params int[] 的地方传递 int[] 是完全有效的。
  • @dtb,您在两个语法点上似乎都是正确的。我从来不知道你可以直接将数组传递给 params 数组参数。
  • @Mikael Svenson,我同意不要担心不安全的代码,但我也认为如果有等效的安全解决方案,应该避免它。

标签: c# arrays iteration padding pinning


【解决方案1】:

多维数组保证是连续的。来自 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:我刚刚仔细检查过,代码运行良好。当然,如果您想避免不安全代码,涉及不安全代码的解决方案无济于事,但我担心没有其他解决方案不会带来很多(不必要的)开销(例如递归迭代所有维度) .
  • ... 但是事实证明,只有当数组包含原始值类型时,固定才有效。我在最初的问题中没有提到这一点,但我试图处理的数组也有一个任意的 ElementType,并且可能包含结构、引用类型或装箱值类型。我想我不得不求助于使用索引数组。
【解决方案2】:

您可以使用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);
}

【讨论】:

  • 是的,这就是我所说的过度算术。您还需要使用 Array.GetLowerBound,而不仅仅是 Array.GetUpperBound。
  • @kennet 实际上,我们不需要需要Array.GetUpperBound,只是看到了Array.GetLength 方法:)
【解决方案3】:

也许您可以将它们全部加入一个临时集合中,然后对其进行迭代。

【讨论】:

  • 很简单,我将使用 GetEnumerator 来处理数组中的所有内容。问题是我还必须在数组中的各个点写入,GetEnumerator 将因此失败。
猜你喜欢
  • 2011-01-19
  • 2010-09-10
  • 2015-01-10
  • 1970-01-01
  • 2016-05-27
  • 1970-01-01
  • 2019-11-27
相关资源
最近更新 更多