如何通过一个二维数组“foreach”?

时间:2022-03-06 23:07:37

I've got a two-dimensional array,

我有一个二维数组,

string[,] table = {
                       { "aa", "aaa" },
                       { "bb", "bbb" }
                   };

And I'd like to foreach through it like this,

我想这样来解释,

foreach (string[] row in table)
{
    Console.WriteLine(row[0] + " " + row[1]);
}

But, I get the error:

但是,我得到了错误:

Can't convert type string to string[]

不能将类型字符串转换为string[]

Is there a way I can achieve what I want, i.e. iterate through the first dimension of the array with the iterator variable returning me the one-dimensional array for that row?

是否有一种方法可以实现我想要的,即迭代数组的第一个维度,迭代器变量返回给我该行的一维数组?

12 个解决方案

#1


21  

If you define your array like this:

如果你这样定义你的数组:

string[][] table = new string[][] { new string[] { "aa", "aaa" }, new string[]{ "bb", "bbb" } };

Then you can use a foreach loop on it.

然后你可以对它使用一个foreach循环。

#2


49  

Multidimensional arrays aren't enumerable. Just iterate the good old-fashioned way:

多维数组不是可点数的。重复一下老式的好方法:

for (int i = 0; i < table.GetLength(0); i++)
{
    Console.WriteLine(table[i, 0] + " " + table[i, 1]);
}

#3


23  

As others have suggested, you could use nested for-loops or redeclare your multidimensional array as a jagged one.

正如其他人所建议的,您可以使用嵌套for循环或将多维数组重新声明为交错数组。

However, I think it's worth pointing out that multidimensional arrays are enumerable, just not in the way that you want. For example:

但是,我认为值得指出的是多维数组是可枚举的,而不是您想要的方式。例如:

string[,] table = {
                      { "aa", "aaa" },
                      { "bb", "bbb" }
                  };

foreach (string s in table)
{
    Console.WriteLine(s);
}

/* Output is:
  aa
  aaa
  bb
  bbb
*/

#4


13  

UPDATE: I had some time on my hands, so ... I went ahead and fleshed out this idea. See below for the code.

更新:我手头有些时间,所以……我进一步充实了这个想法。参见下面的代码。


Here's a bit of a crazy answer:

这里有一个疯狂的答案:

You could do what you're looking for -- essentially treat a two-dimensional array as a table with rows -- by writing a static method (perhaps an extension method) that takes a T[,] and returns an IEnumerable<T[]>. This would require copying each "row" of the underlying table into a new array, though.

您可以通过编写一个静态方法(可能是扩展方法)来完成您所需要的工作——本质上,将一个二维数组视为一个具有行数的表,该方法接受T[,]并返回一个IEnumerable [T]>。不过,这需要将底层表的每个“行”复制到一个新的数组中。

A perhaps better (though more involved) approach would be to actually write a class that implements IList<T> as a wrapper around a single "row" of a two-dimensional array (you would probably set IsReadOnly to true and just implement the getter for the this[int] property and probably Count and GetEnumerator; everything else could throw a NotSupportedException). Then your static/extension method could return an IEnumerable<IList<T>> and provide deferred execution.

一个更好的(尽管更复杂的)方法是实际编写一个类,它将IList 作为一个封装在一个二维数组中的一个“行”的包装(您可能会将IsReadOnly设置为true,并为这个[int]属性实现getter,并可能计算和GetEnumerator;其他任何东西都可能抛出NotSupportedException异常)。然后,您的静态/扩展方法可以返回一个IEnumerable >,并提供延迟执行。

That way you could write code pretty much like what you have:

这样你就可以像你所拥有的一样编写代码:

foreach (IList<string> row in table.GetRows()) // or something
{
    Console.WriteLine(row[0] + " " + row[1]);
}

Just a thought.

只是一个想法。


Implementation suggestion:

实施建议:

public static class ArrayTableHelper {
    public static IEnumerable<IList<T>> GetRows<T>(this T[,] table) {
        for (int i = 0; i < table.GetLength(0); ++i)
            yield return new ArrayTableRow<T>(table, i);
    }

    private class ArrayTableRow<T> : IList<T> {
        private readonly T[,] _table;
        private readonly int _count;
        private readonly int _rowIndex;

        public ArrayTableRow(T[,] table, int rowIndex) {
            if (table == null)
                throw new ArgumentNullException("table");

            if (rowIndex < 0 || rowIndex >= table.GetLength(0))
                throw new ArgumentOutOfRangeException("rowIndex");

            _table = table;
            _count = _table.GetLength(1);
            _rowIndex = rowIndex;
        }

        // I didn't implement the setter below,
        // but you easily COULD (and then set IsReadOnly to false?)
        public T this[int index] {
            get { return _table[_rowIndex, index]; }
            set { throw new NotImplementedException(); }
        }

        public int Count {
            get { return _count; }
        }

        bool ICollection<T>.IsReadOnly {
            get { return true; }
        }

        public IEnumerator<T> GetEnumerator() {
            for (int i = 0; i < _count; ++i)
                yield return this[i];
        }

        // omitted remaining IList<T> members for brevity;
        // you actually could implement IndexOf, Contains, etc.
        // quite easily, though
    }
}

...now I think I should give * a break for the rest of the day ;)

…现在我想我应该让*在今天剩下的时间里休息一下;

#5


3  

string[][] table = { ... };

#6


3  

It depends on how you define your multi-dimensional array. Here are two options:

这取决于如何定义多维数组。这里有两个选择:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            // First
            string[,] arr1 = {
                       { "aa", "aaa" },
                       { "bb", "bbb" }
                   };

            // Second
            string[][] arr2 = new[] {
                new[] { "aa", "aaa" },
                new[] { "bb", "bbb" }
            };

            // Iterate through first
            for (int x = 0; x <= arr1.GetUpperBound(0); x++)
                for (int y = 0; y <= arr1.GetUpperBound(1); y++)
                    Console.Write(arr1[x, y] + "; ");

            Console.WriteLine(Environment.NewLine);

            // Iterate through second second
            foreach (string[] entry in arr2)
                foreach (string element in entry)
                    Console.Write(element + "; ");

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Press any key to finish");
            Console.ReadKey();
        }
    }
}

#7


2  

Using LINQ you can do it like this:

使用LINQ,你可以这样做:

var table_enum = table

    // Convert to IEnumerable<string>
    .OfType<string>()

    // Create anonymous type where Index1 and Index2
    // reflect the indices of the 2-dim. array
    .Select((_string, _index) => new {
        Index1 = (_index / 2),
        Index2 = (_index % 2), // ← I added this only for completeness
        Value = _string
    })

    // Group by Index1, which generates IEnmurable<string> for all Index1 values
    .GroupBy(v => v.Index1)

    // Convert all Groups of anonymous type to String-Arrays
    .Select(group => group.Select(v => v.Value).ToArray());

// Now you can use the foreach-Loop as you planned
foreach(string[] str_arr in table_enum) {
    // …
}

This way it is also possible to use the foreach for looping through the columns instead of the rows by using Index2 in the GroupBy instead of Index 1. If you don't know the dimension of your array then you have to use the GetLength() method to determine the dimension and use that value in the quotient.

这样,通过在GroupBy中使用Index2而不是索引1,也可以使用foreach循环遍历列而不是行。如果不知道数组的维数,则必须使用GetLength()方法确定维数,并在除法中使用该值。

#8


1  

string[][] languages = new string[2][];
            languages[0] = new string[2];
            languages[1] = new string[3];
// inserting data into double dimensional arrays.
            for (int i = 0; i < 2; i++)
            {
                languages[0][i] = "Jagged"+i.ToString();
            }
            for (int j = 0; j < 3; j++)
            {
                languages[1][j] = "Jag"+j.ToString();
            }

// doing foreach through 2 dimensional arrays.
foreach (string[] s in languages)
            {
                foreach (string a in s)
                {
                    Console.WriteLine(a);
                }
            }

#9


1  

Remember that a multi-dimensional array is like a table. You don't have an x element and a y element for each entry; you have a string at (for instance) table[1,2].

记住,多维数组就像一个表。没有x元素和y元素;在(例如)表中有一个字符串[1,2]。

So, each entry is still only one string (in your example), it's just an entry at a specific x/y value. So, to get both entries at table[1, x], you'd do a nested for loop. Something like the following (not tested, but should be close)

因此,每个条目仍然是一个字符串(在您的示例中),它只是一个特定x/y值的条目。因此,要在表[1,x]中获得这两个条目,需要做一个嵌套的for循环。如下所示(未测试,但应该接近)

for (int x = 0; x < table.Length; x++)
{
    for (int y = 0; y < table.Length; y += 2)
    {
        Console.WriteLine("{0} {1}", table[x, y], table[x, y + 1]);
    }
}

#10


1  

Here's a simple extension method that returns each row as an IEnumerable<T>. This has the advantage of not using any extra memory:

这里有一个简单的扩展方法,它将每一行返回为IEnumerable 。这样做的好处是不用任何额外的内存:

public static class Array2dExt
{
    public static IEnumerable<IEnumerable<T>> Rows<T>(this T[,] array)
    {
        for (int r = array.GetLowerBound(0); r <= array.GetUpperBound(0); ++r)
            yield return row(array, r);
    }

    static IEnumerable<T> row<T>(T[,] array, int r)
    {
        for (int c = array.GetLowerBound(1); c <= array.GetUpperBound(1); ++c)
            yield return array[r, c];
    }
}

Sample usage:

示例用法:

static void Main()
{
    string[,] siblings = { { "Mike", "Amy" }, { "Mary", "Albert" }, {"Fred", "Harry"} };

    foreach (var row in siblings.Rows())
        Console.WriteLine("{" + string.Join(", ", row) + "}");
}

#11


0  

I try this. I hope to help. It work with

我试试这个。我希望能有所帮助。它的工作

static void Main()
    {
        string[,] matrix = {
                               { "aa", "aaa" },
                               { "bb", "bbb" }
                           };
        int index = 0;
        foreach (string element in matrix)
        {
            if (index < matrix.GetLength(1))
            {
                Console.Write(element);
                if (index < (matrix.GetLength(1) - 1))
                {
                    Console.Write(" ");
                }
                index++;
            }
            if (index == matrix.GetLength(1))
            {
                Console.Write("\n");
                index = 0;
            }
        }

#12


0  

I'm not a big fan of this method because of the memory usage involved, but if you use the arrays it produces, it isn't such a waste.

我不太喜欢这个方法,因为它涉及到内存的使用,但是如果您使用它生成的数组,这并不是一种浪费。

public static void ForEachRow<T>(this T[,] list, Action<int, T[]> action)
{
    var len = list.GetLength(0);
    var sub = list.GetLength(1);

    T[] e;
    int i, j;

    for (i = 0; i < len; i++)
    {
        e = new T[sub];

        for (j = 0; j < sub; j++)
        {
            e[j] = list[i, j];
        }

        action(i, e);
    }
}

Implementation:

实现:

var list = new[,]{0x0, 0x1, 0x2, 0x4, 0x8};

list.ForEachRow((i, row) =>
{
    for (var j = 0; j < row.Length; j++)
    {
        Console.WriteLine("[{0},{1}]: {2}", i, j, row[j]);
    }
});

The other solution I found is less memory intensive, but will use more CPU, especially when the dimensions of the arrays' entries are larger.

我发现的另一种解决方案内存占用较少,但会使用更多的CPU,特别是当数组条目的维度较大时。

public static void ForEachRow<T>(this T[,] list, Action<int, IEnumerable<T>> action)
{
    var len = list.GetLength(0);
    var sub = list.GetLength(1);

    int i, j;
    IEnumerable<T> e;

    for (i = 0; i < len; i++)
    {
        e = Enumerable.Empty<T>();

        for (j = 0; j < sub; j++)
        {
            e = e.Concat(AsEnumerable(list[i, j]));
        }

        action(i, e);
    }
}

private static IEnumerable<T> AsEnumerable<T>(T add)
{
    yield return add;
}

Implementation:

实现:

var list = new[,]{0x0, 0x1, 0x2, 0x4, 0x8};

list.ForEachRow((i, row) =>
{
    var j = 0;

    forrach (var o in row)
    {
        Console.WriteLine("[{0},{1}]: {2}", i, j, o);

        ++j;
    }
});

As a whole, I find the first option to be more intuitive, especially if you want to access the produced array by its indexer.

总的来说,我发现第一个选项更加直观,特别是如果您希望通过索引器访问生成的数组。

At the end of the day, this is all just eye candy, neither methods should really be used in favour of directly accessing the source array;

最后,这些都只是小菜一碟,两种方法都不应该用于直接访问源数组;

for (var i = 0; i < list.GetLength(0); i++)
{
    foreach (var j = 0; j < list.GetLength(1); j++)
    {
        Console.WriteLine("[{0},{1}]: {2}", i, j, list[i, j]);
    }
}

#1


21  

If you define your array like this:

如果你这样定义你的数组:

string[][] table = new string[][] { new string[] { "aa", "aaa" }, new string[]{ "bb", "bbb" } };

Then you can use a foreach loop on it.

然后你可以对它使用一个foreach循环。

#2


49  

Multidimensional arrays aren't enumerable. Just iterate the good old-fashioned way:

多维数组不是可点数的。重复一下老式的好方法:

for (int i = 0; i < table.GetLength(0); i++)
{
    Console.WriteLine(table[i, 0] + " " + table[i, 1]);
}

#3


23  

As others have suggested, you could use nested for-loops or redeclare your multidimensional array as a jagged one.

正如其他人所建议的,您可以使用嵌套for循环或将多维数组重新声明为交错数组。

However, I think it's worth pointing out that multidimensional arrays are enumerable, just not in the way that you want. For example:

但是,我认为值得指出的是多维数组是可枚举的,而不是您想要的方式。例如:

string[,] table = {
                      { "aa", "aaa" },
                      { "bb", "bbb" }
                  };

foreach (string s in table)
{
    Console.WriteLine(s);
}

/* Output is:
  aa
  aaa
  bb
  bbb
*/

#4


13  

UPDATE: I had some time on my hands, so ... I went ahead and fleshed out this idea. See below for the code.

更新:我手头有些时间,所以……我进一步充实了这个想法。参见下面的代码。


Here's a bit of a crazy answer:

这里有一个疯狂的答案:

You could do what you're looking for -- essentially treat a two-dimensional array as a table with rows -- by writing a static method (perhaps an extension method) that takes a T[,] and returns an IEnumerable<T[]>. This would require copying each "row" of the underlying table into a new array, though.

您可以通过编写一个静态方法(可能是扩展方法)来完成您所需要的工作——本质上,将一个二维数组视为一个具有行数的表,该方法接受T[,]并返回一个IEnumerable [T]>。不过,这需要将底层表的每个“行”复制到一个新的数组中。

A perhaps better (though more involved) approach would be to actually write a class that implements IList<T> as a wrapper around a single "row" of a two-dimensional array (you would probably set IsReadOnly to true and just implement the getter for the this[int] property and probably Count and GetEnumerator; everything else could throw a NotSupportedException). Then your static/extension method could return an IEnumerable<IList<T>> and provide deferred execution.

一个更好的(尽管更复杂的)方法是实际编写一个类,它将IList 作为一个封装在一个二维数组中的一个“行”的包装(您可能会将IsReadOnly设置为true,并为这个[int]属性实现getter,并可能计算和GetEnumerator;其他任何东西都可能抛出NotSupportedException异常)。然后,您的静态/扩展方法可以返回一个IEnumerable >,并提供延迟执行。

That way you could write code pretty much like what you have:

这样你就可以像你所拥有的一样编写代码:

foreach (IList<string> row in table.GetRows()) // or something
{
    Console.WriteLine(row[0] + " " + row[1]);
}

Just a thought.

只是一个想法。


Implementation suggestion:

实施建议:

public static class ArrayTableHelper {
    public static IEnumerable<IList<T>> GetRows<T>(this T[,] table) {
        for (int i = 0; i < table.GetLength(0); ++i)
            yield return new ArrayTableRow<T>(table, i);
    }

    private class ArrayTableRow<T> : IList<T> {
        private readonly T[,] _table;
        private readonly int _count;
        private readonly int _rowIndex;

        public ArrayTableRow(T[,] table, int rowIndex) {
            if (table == null)
                throw new ArgumentNullException("table");

            if (rowIndex < 0 || rowIndex >= table.GetLength(0))
                throw new ArgumentOutOfRangeException("rowIndex");

            _table = table;
            _count = _table.GetLength(1);
            _rowIndex = rowIndex;
        }

        // I didn't implement the setter below,
        // but you easily COULD (and then set IsReadOnly to false?)
        public T this[int index] {
            get { return _table[_rowIndex, index]; }
            set { throw new NotImplementedException(); }
        }

        public int Count {
            get { return _count; }
        }

        bool ICollection<T>.IsReadOnly {
            get { return true; }
        }

        public IEnumerator<T> GetEnumerator() {
            for (int i = 0; i < _count; ++i)
                yield return this[i];
        }

        // omitted remaining IList<T> members for brevity;
        // you actually could implement IndexOf, Contains, etc.
        // quite easily, though
    }
}

...now I think I should give * a break for the rest of the day ;)

…现在我想我应该让*在今天剩下的时间里休息一下;

#5


3  

string[][] table = { ... };

#6


3  

It depends on how you define your multi-dimensional array. Here are two options:

这取决于如何定义多维数组。这里有两个选择:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            // First
            string[,] arr1 = {
                       { "aa", "aaa" },
                       { "bb", "bbb" }
                   };

            // Second
            string[][] arr2 = new[] {
                new[] { "aa", "aaa" },
                new[] { "bb", "bbb" }
            };

            // Iterate through first
            for (int x = 0; x <= arr1.GetUpperBound(0); x++)
                for (int y = 0; y <= arr1.GetUpperBound(1); y++)
                    Console.Write(arr1[x, y] + "; ");

            Console.WriteLine(Environment.NewLine);

            // Iterate through second second
            foreach (string[] entry in arr2)
                foreach (string element in entry)
                    Console.Write(element + "; ");

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Press any key to finish");
            Console.ReadKey();
        }
    }
}

#7


2  

Using LINQ you can do it like this:

使用LINQ,你可以这样做:

var table_enum = table

    // Convert to IEnumerable<string>
    .OfType<string>()

    // Create anonymous type where Index1 and Index2
    // reflect the indices of the 2-dim. array
    .Select((_string, _index) => new {
        Index1 = (_index / 2),
        Index2 = (_index % 2), // ← I added this only for completeness
        Value = _string
    })

    // Group by Index1, which generates IEnmurable<string> for all Index1 values
    .GroupBy(v => v.Index1)

    // Convert all Groups of anonymous type to String-Arrays
    .Select(group => group.Select(v => v.Value).ToArray());

// Now you can use the foreach-Loop as you planned
foreach(string[] str_arr in table_enum) {
    // …
}

This way it is also possible to use the foreach for looping through the columns instead of the rows by using Index2 in the GroupBy instead of Index 1. If you don't know the dimension of your array then you have to use the GetLength() method to determine the dimension and use that value in the quotient.

这样,通过在GroupBy中使用Index2而不是索引1,也可以使用foreach循环遍历列而不是行。如果不知道数组的维数,则必须使用GetLength()方法确定维数,并在除法中使用该值。

#8


1  

string[][] languages = new string[2][];
            languages[0] = new string[2];
            languages[1] = new string[3];
// inserting data into double dimensional arrays.
            for (int i = 0; i < 2; i++)
            {
                languages[0][i] = "Jagged"+i.ToString();
            }
            for (int j = 0; j < 3; j++)
            {
                languages[1][j] = "Jag"+j.ToString();
            }

// doing foreach through 2 dimensional arrays.
foreach (string[] s in languages)
            {
                foreach (string a in s)
                {
                    Console.WriteLine(a);
                }
            }

#9


1  

Remember that a multi-dimensional array is like a table. You don't have an x element and a y element for each entry; you have a string at (for instance) table[1,2].

记住,多维数组就像一个表。没有x元素和y元素;在(例如)表中有一个字符串[1,2]。

So, each entry is still only one string (in your example), it's just an entry at a specific x/y value. So, to get both entries at table[1, x], you'd do a nested for loop. Something like the following (not tested, but should be close)

因此,每个条目仍然是一个字符串(在您的示例中),它只是一个特定x/y值的条目。因此,要在表[1,x]中获得这两个条目,需要做一个嵌套的for循环。如下所示(未测试,但应该接近)

for (int x = 0; x < table.Length; x++)
{
    for (int y = 0; y < table.Length; y += 2)
    {
        Console.WriteLine("{0} {1}", table[x, y], table[x, y + 1]);
    }
}

#10


1  

Here's a simple extension method that returns each row as an IEnumerable<T>. This has the advantage of not using any extra memory:

这里有一个简单的扩展方法,它将每一行返回为IEnumerable 。这样做的好处是不用任何额外的内存:

public static class Array2dExt
{
    public static IEnumerable<IEnumerable<T>> Rows<T>(this T[,] array)
    {
        for (int r = array.GetLowerBound(0); r <= array.GetUpperBound(0); ++r)
            yield return row(array, r);
    }

    static IEnumerable<T> row<T>(T[,] array, int r)
    {
        for (int c = array.GetLowerBound(1); c <= array.GetUpperBound(1); ++c)
            yield return array[r, c];
    }
}

Sample usage:

示例用法:

static void Main()
{
    string[,] siblings = { { "Mike", "Amy" }, { "Mary", "Albert" }, {"Fred", "Harry"} };

    foreach (var row in siblings.Rows())
        Console.WriteLine("{" + string.Join(", ", row) + "}");
}

#11


0  

I try this. I hope to help. It work with

我试试这个。我希望能有所帮助。它的工作

static void Main()
    {
        string[,] matrix = {
                               { "aa", "aaa" },
                               { "bb", "bbb" }
                           };
        int index = 0;
        foreach (string element in matrix)
        {
            if (index < matrix.GetLength(1))
            {
                Console.Write(element);
                if (index < (matrix.GetLength(1) - 1))
                {
                    Console.Write(" ");
                }
                index++;
            }
            if (index == matrix.GetLength(1))
            {
                Console.Write("\n");
                index = 0;
            }
        }

#12


0  

I'm not a big fan of this method because of the memory usage involved, but if you use the arrays it produces, it isn't such a waste.

我不太喜欢这个方法,因为它涉及到内存的使用,但是如果您使用它生成的数组,这并不是一种浪费。

public static void ForEachRow<T>(this T[,] list, Action<int, T[]> action)
{
    var len = list.GetLength(0);
    var sub = list.GetLength(1);

    T[] e;
    int i, j;

    for (i = 0; i < len; i++)
    {
        e = new T[sub];

        for (j = 0; j < sub; j++)
        {
            e[j] = list[i, j];
        }

        action(i, e);
    }
}

Implementation:

实现:

var list = new[,]{0x0, 0x1, 0x2, 0x4, 0x8};

list.ForEachRow((i, row) =>
{
    for (var j = 0; j < row.Length; j++)
    {
        Console.WriteLine("[{0},{1}]: {2}", i, j, row[j]);
    }
});

The other solution I found is less memory intensive, but will use more CPU, especially when the dimensions of the arrays' entries are larger.

我发现的另一种解决方案内存占用较少,但会使用更多的CPU,特别是当数组条目的维度较大时。

public static void ForEachRow<T>(this T[,] list, Action<int, IEnumerable<T>> action)
{
    var len = list.GetLength(0);
    var sub = list.GetLength(1);

    int i, j;
    IEnumerable<T> e;

    for (i = 0; i < len; i++)
    {
        e = Enumerable.Empty<T>();

        for (j = 0; j < sub; j++)
        {
            e = e.Concat(AsEnumerable(list[i, j]));
        }

        action(i, e);
    }
}

private static IEnumerable<T> AsEnumerable<T>(T add)
{
    yield return add;
}

Implementation:

实现:

var list = new[,]{0x0, 0x1, 0x2, 0x4, 0x8};

list.ForEachRow((i, row) =>
{
    var j = 0;

    forrach (var o in row)
    {
        Console.WriteLine("[{0},{1}]: {2}", i, j, o);

        ++j;
    }
});

As a whole, I find the first option to be more intuitive, especially if you want to access the produced array by its indexer.

总的来说,我发现第一个选项更加直观,特别是如果您希望通过索引器访问生成的数组。

At the end of the day, this is all just eye candy, neither methods should really be used in favour of directly accessing the source array;

最后,这些都只是小菜一碟,两种方法都不应该用于直接访问源数组;

for (var i = 0; i < list.GetLength(0); i++)
{
    foreach (var j = 0; j < list.GetLength(1); j++)
    {
        Console.WriteLine("[{0},{1}]: {2}", i, j, list[i, j]);
    }
}