【问题标题】:How to overload foreach such that it ignores nulls in a 2D array如何重载 foreach 使其忽略二维数组中的空值
【发布时间】:2015-05-18 03:50:29
【问题描述】:

我有一个像这样声明的二维对象数组:

public PCE[,] PARR = new PCE[64, 64]; // object is PCE, a piece, PARR is short for piece array. The 2D array is used to quickly get a piece at a certain position on the board.

我知道你可以使用“foreach”来遍历列表和数组,所以我试了一下。结果它会遍历每个元素,包括空值(我猜它被认为是元素?),这意味着我必须先进行空值检查。

重载“foreach”运算符来做两件事会很好:

  1. 排除空值
  2. 不是以 63 x 63 的方式限制 foreach,而是以每个级别的特定板尺寸。 (64 x 64 只是最大的电路板尺寸。大多数电路板会小得多。)

这可能吗?我在 Visual Studio 2010 中使用 C# MonoGame。

附言。在我忘记之前,我希望 foreach 按顺序进行迭代,就好像我在执行以下嵌套循环一样:

for (int j = 0; j < bh; j += 1) // bh is board height
{
    for (int i = 0; i < bw; i += 1) // bw is board width
    {
        // this iterates from left to right, then top to bottom
    }
}

【问题讨论】:

  • 您询问的是foreach,但您的代码显示for 循环。你试过foreach吗?

标签: c#


【解决方案1】:

你不能超载foreach。它不是一种方法(操作员或其他)。

您可以编写一个迭代器方法,然后您可以在foreach 循环中调用并使用它的返回值。例如:

IEnumerable<T> EnumerateSubMatrix<T>(T[,] array, int width, int height)
{
    for (int i = 0; i < height; i++)
    for (int j = 0; j < width; j++)
    {
        yield return array[i, j];
    }
}

那么就:

foreach (PCE pce in EnumerateSubMatrix(PARR, bw, bh))
{
    // something
}

如果要排除null 值,只需在迭代器方法中跳过它们即可:

IEnumerable<T> EnumerateSubMatrix<T>(T[,] array, int width, int height)
    where T : class
{
    for (int i = 0; i < height; i++)
    for (int j = 0; j < width; j++)
    {
        T t = array[i, j];

        if (t != null)
        {
            yield return t;
        }
    }
}

请注意,您必须在T 上添加一个约束,以确保它是一个引用类型,以便它甚至可以与null 进行比较。

【讨论】:

  • 我不明白你代码的最后一部分。看起来您正在测试整个数组是否为空,这与测试元素之一是否为空不同。另外,你能解释一下 yield 关键字的作用吗?如果我们返回整个数组,那真的会让我感到困惑,因为它会有空元素,除非我们以某种方式从数组中删除元素,使其呈锯齿状。
  • 局部变量t 是数组的当前元素,而不是整个数组本身。我确实忘记编辑yield return (从我给出的原始示例中复制/粘贴),但我已经编辑了问题以清理它(无论哪种方式都可以,但局部变量的重点是避免取消引用数组两次)。不,它没有返回整个数组; yield 关键字是使此方法 an iterator method 的原因。单击链接以阅读有关它的更多信息。 :)
【解决方案2】:

这是一个帮助类和使用它的示例代码。我用的是字符串而不是 PCE。

public static class BoardExtensions
{
    public class IterationElement<T>
    {
        protected T[,] Board { get; set; }

        public int XPos { get; protected set; }
        public int YPos { get; protected set; }

        public T Element
        {
            get { return Board[XPos, YPos]; }
            set { Board[XPos, YPos] = value; }
        }

        public IterationElement(T[,] board, int x, int y)
        {
            this.Board = board;
            this.XPos = x;
            this.YPos = y;
        }
    }

    public static IEnumerable<IterationElement<T>> ValidItems<T>(this T[,] board, int width, int height)
    {
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                if (board[x, y] != null)
                {
                    yield return new IterationElement<T>(board, x, y);
                }
            }
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var random = new Random();
        var board = new string[64, 64];

        for (int x = 0; x < 64; x++)
        {
            for (int y = 0; y < 64; y++)
            {
                if (random.Next() % 2 == 0)
                {
                    board[x, y] = "OK";
                }
            }
        }

        foreach (var item in board.ValidItems(10, 15))
        {
            Console.WriteLine(string.Format("[{0},{1}]: {2}", item.XPos, item.YPos, item.Element));
        }
        Console.ReadKey(true);
    }
}

【讨论】:

    【解决方案3】:

    我认为解决方案是使用稀疏数组。使用Dictionary&lt;int,PCE&gt; 存储每个对象及其位置。所以如果板子是N×N来指定[i,j]位置使用key:i*N+j。

    请看下面的例子:

    public class PCE { }
    public class Board
    {
        readonly int size;
        readonly Dictionary<int, PCE> pieces;
    
        public Board(int size)
        {
            this.size=size;
            pieces=new Dictionary<int, PCE>();
        }
        public int Size { get { return size; } }
        public int GetKeyFromPosition(int i, int j) { return size*i+j; }
        public void GetPositionFromKey(int key, out int i, out int j)
        {
            i=key/size;
            j=key%size;
        }
        public void PlacePCE(int i, int j, PCE item)
        {
            pieces[size*i+j]=item;
        }
    
        public PCE GetPCE(int i, int j)
        {
            if (pieces.ContainsKey(size*i+j))
            {
                return pieces[size*i+j];
            }
            return null;
        }
        public PCE this[int i, int j]
        {
            get { return GetPCE(i, j); }
            set { PlacePCE(i, j, value); }
        }
    
        public bool IsLocationFilled(int i, int j)
        {
            return pieces.ContainsKey(size*i+j);
        }
    
        public List<KeyValuePair<int, PCE>> Pieces
        {
            get
            {
                return pieces.ToList();
            }
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Board board=new Board(64);
    
            board.PlacePCE(4, 7, new PCE());
            board.PlacePCE(11, 2, new PCE());
            board.PlacePCE(2, 47, new PCE());
            board.PlacePCE(51, 37, new PCE());
    
            if (board.IsLocationFilled(2, 47))
            {
                var pce=board[2, 47];
            }
    
            // Non-nulls only. Iterates over 4 items only
            foreach (var item in board.Pieces)
            {
                int i, j;
                board.GetPosition(item.Key, out i, out j);
                PCE pce=item.Value;
                // Do something with [i,j] PCE
            }
        }
    }
    

    特点:

    1. 可变板尺寸(您需要在构造函数中为Board 设置限制)
    2. GetKeyFromPosition 和 GetPositionFromKey 在二维坐标之间转换为一维唯一键位置(并返回)。
    3. PlacePCE 和 GetPCE 在板位置设置 PCE 类型(或检索它)。 this[int i, int j] 也可以这样做,因此您可以使用 Board[,] 索引
    4. IsLocationFilled 检查位置是否已包含项目
    5. Pieces 通过KeyValuePair 返回板上项目的列表。对键是位置索引,值是PCE 实例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-28
      • 1970-01-01
      • 2015-08-02
      • 1970-01-01
      • 1970-01-01
      • 2015-04-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多