【问题标题】:Initialize multidimensional array of objects in c#在 C# 中初始化对象的多维数组
【发布时间】:2015-08-23 01:52:14
【问题描述】:

我只是想知道,有没有更好的方法来初始化 c# 多维对象数组(引用类型)。

这是我的代码:

Board = new Field[BoardHeight, BoardWidth];

for (int i = 0; i < BoardHeight; i++)
{
    for (int j = 0; j < BoardWidth; j++)
    {
        Board[i, j] = new Field();
    }
}

如果我可以摆脱 for/for 循环并用单行替换它?那太好了。

【问题讨论】:

  • 应该在Code Review上询问。
  • 方法看起来不错。为什么不用它制作一个实用方法并在需要时使用它?
  • 你不能在一个平面循环中循环并初始化数组吗?我认为Length 会暴露数组的总长度,你可以在Rank 上使用模数来找出你需要的索引
  • @Asad 你可以(即for (var i = 0; i &lt; BoardHeight* BoardWidth; i++) { Board[i/BoardWidth, i%BoardWidth] = new Field();})但它的可读性较差(而且不太可能更快)
  • @AlexeiLevenkov 是的,但是它可以很容易地将其提取到任意等级数组的扩展方法中。顺便说一句,你不需要乘法,我认为 .Length 会起作用。

标签: c# .net arrays multidimensional-array


【解决方案1】:

嵌套的for 循环通常是初始化二维数组最易读/接受的方法。

如果您确实需要单个语句 - 一种选择是使用Enumerable.Aggregate(在单个语句中创建/填充数组)和Enumerable.Range + Enumerable.SelectMany(使用How do I take the Cartesian join of two lists in c#? 创建索引):

 Board = Enumerable.Range(0, BoardHeight)
         .SelectMany(x => Enumerable.Range(0, BoardWidth), (row,col)=>new {row,col})
         .Aggregate(new Field[BoardHeight, BoardWidth],
             (board, position)=> 
               {
                   board[position.row,position.col] = new Field(); 
                   return board;
               });

(此代码的可读性有待讨论)

更实用的解决方案:

for (var i = 0; i < BoardHeight* BoardWidth; i++) 
{
    Board[i / BoardWidth, i % BoardWidth] = new Field();
}

如果您真的需要“单行初始化” - 将嵌套的 for 循环重构为方法(可能是通用的)并使用“单行”在任何您想要的地方调用它。比如:

   public TField[,] InitializeArray<TField>(
      int BoardHeight, int BoardWidth) where TField:new()
   {
        var Board = new TField[BoardHeight, BoardWidth];

        for (int i = 0; i < BoardHeight; i++)
        {
            for (int j = 0; j < BoardWidth; j++)
            {
                Board[i, j] = new TField();
            }
        }
        return Board;
}

【讨论】:

  • 哇,太丑了。我永远不会在任何严肃的代码库中使用这样的代码。但是,是的,解决了 OP 问题。
  • @MarcinJuraszek 当然。我不会将它用于任何真正的代码,而是作为单行练习:) - 我只是喜欢Aggregate,但从来没有找到在实际代码中使用它的地方。
【解决方案2】:

出于好奇,我将一些旧代码改编为扩展方法,该方法将使用提供的Func 的结果初始化任意等级数组的每个成员。你会像这样使用它(使用 2D、3D、任何 D 数组):

var board = new Foo[10, 20];
board.Fill(() => new Foo());

扩展方法如下:

static class Extensions
{
    public static void Fill(this Array arr, Func<object> gen)
    {
        // Get the dimensions of the array
        var dims = Enumerable.Range(0, arr.Rank)
            .Select(arr.GetLength)
            .ToArray();

        Func<int, int, int> product = (i1, i2) => i1 * i2;

        for (var i = 0; i < arr.Length; i++)
        {
            var indices = dims.Select((d, n) => (i/dims.Take(n).Aggregate(1, product))%d).ToArray();
            arr.SetValue(gen(), indices);
        }
    }
}

【讨论】:

    【解决方案3】:

    感谢MarcinJuraszekAlexei Levenkov 的建议,我得出了这个答案。从技术上讲不是一个单一的语句(总而言之),但如果你隐藏扩展方法;-) 它看起来很干净:

    Board = new Field[BoardHeight, BoardWidth];
    Board.Init();
    

    把它藏起来:

    public static class MyExtensions
    {
        public static void Init<T>(this T[,] board) where T : new()
        {
            for (int i = 0; i < board.GetLength(0); i++)
            {
                for (int j = 0; j < board.GetLength(1); j++)
                {
                    board[i,j] = new T();
                }
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      我认为这是初始化数组的最简单的单行代码(为了便于阅读,拆分为多行):

      var board = Enumerable.Range(0, BoardWidth)
          .Select(row => Enumerable.Range(0, BoardHeight)
              .Select(value =>new TField())
              .ToArray())
          .ToArray();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-03
        • 2015-02-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-01
        相关资源
        最近更新 更多