C#循环遍历一个未知维度的数组

3

我想创建一个扩展方法来循环遍历未知维度的System.Array

目前我采用了一个简单粗暴的方法:

public static void ForEach<T>(this Array source, Action<T> action)
{
    if(source.Rank == 1)
    {
        for (int w = 0; w < source.GetLength(0); w++)
        {
            action((T)source.GetValue(w));
        }
    }
    else if(source.Rank == 2)
    {
        for (int h = 0; h < source.GetLength(1); h++)
        {
            for (int w = 0; w < source.GetLength(0); w++)
            {
                action((T)source.GetValue(h, w));
            }
        }
    }
    else if(source.Rank == 3)
    {
        // etc
    }
}

我相信,有更加优雅的方法来做这件事。但是我无法想出来。如何将该方法推广到无限数量的维度?


1
可能是为什么C#多维数组没有实现IEnumerable<T>?的重复问题。 - mjwills
@ChristianGollhardt,使用面向对象编程语言的多维数组有什么“根本性问题”?如果没有多维数组,你如何进行张量操作呢? - koryakinp
看起来我误解了你的问题。我把维度和维度的大小搞混了。@koryakinp - Christian Gollhardt
3个回答

1
如果你不关心索引,你可以遍历一个 System.Array,而完全不需要了解它的秩。枚举器会访问每个元素。
public class Program
{
    public static void IterateOverArray(System.Array a)
    {
        foreach (var i in a)
        {
            Console.WriteLine(i);
        }
    }

    public static void Main()
    {
        var tests = new System.Array []
        {
            new int[] {1,2,3,4,5,6,7,8},
            new int[,]
            {
                {1,2},{3,4},{5,6},{7,8}
            },
            new int[,,]
            {
                {  {1,2},{3,4} },
                {  {5,6},{7,8} }
            }
        };


        foreach (var t in tests)
        {
            Console.WriteLine("Dumping array with rank {0} to console.", t.Rank);
            IterateOverArray(t);
        }
    }
}

输出:

Dumping array with rank 1 to console.
1
2
3
4
5
6
7
8
Dumping array with rank 2 to console.
1
2
3
4
5
6
7
8
Dumping array with rank 3 to console.
1
2
3
4
5
6
7
8

链接到DotNetFiddle示例


1

对于在家玩的你们来说,这可能有点混乱,但它允许你使用yield利用foreach遍历等级。

public static IEnumerable<T> GetRank<T>(this Array source,int dimension, params int[] indexes )
{

   var indexList = indexes.ToList();
   indexList.Insert(dimension, 0);
   indexes = indexList.ToArray();

   for (var i = 0; i < source.GetLength(dimension); i++)
   {
      indexes[dimension] = i;
      yield return (T)source.GetValue(indexes);
   }
}

使用方法

var test1 = new int[2, 2, 3];
test1[1, 1, 0] = 1;
test1[1, 1, 1] = 2;
test1[1, 1, 2] = 3;
foreach (var item in test1.GetRank<int>(2,1,1))
{
  Console.WriteLine(item);
}

输出

1
2
3

完整演示在此处


-1
你可以尝试使用递归方法,测试数组中的项是否是一个数组。如果该项可迭代,则调用for循环逻辑,否则您可以对该项进行任何需要的操作。如果您的对象实现了ICollection接口,这应该相当简单。

我认为zcleghern的意思是这样的: public static void ForEach(this Array source, Action action) { if (source.Rank == 0) { foreach (var item in source) { action((T) item); } } else { foreach (Array arr in source) { arr.ForEach(action); } } } - ryzngard

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