如何使 class 在 C# 中用作二维数组?

How do I make a class usable as a 2d array in c#?

代码

Class

using System;
using System.Collections.Generic;

// In case you really, really want to know,
// this class is called Ticket in my real code and it's used as a Tambola ticket.
public class Foo
{
    public int?[,] Value { get; private set; } = new int?[3 , 9]

    public Foo () => Value = InternalGenerate()

    public static Foo Generate () => new() { Value = InternalGenerate() };
    protected static int?[,] InternalGenerate ()
    {
        // Generate a random instance of this class.
    }
}

用法

class Program
{
    static void Main ()
    {
        Bar( new() );
    }

    static void Bar ( Foo foo )
    {
        int width = foo.GetLength(1);
        int height = foo.GetLength(0);
        int?[] array = foo.Cast<int?>().ToArray(); // convert foo to 1d array.

        // get "corners" of foo
        int tl = (int) array.First(e => e.HasValue);
        int fl = (int) array.Take(width).Last(e => e.HasValue);
        int lf = (int) array.Skip(( height - 1 ) * width).First(e => e.HasValue);
        int ll = (int) array.Last(e => e.HasValue);
        // this code comes from
        // whosebug.com/questions/68661235/get-corners-of-a-2d-array
    }
}

此代码将失败。


问题

我有一个叫 Foo 的 class。 它的实际值 应该 是类型 int? 的二维数组 - 但是,当我尝试将 Foo 的实例用作 int?[,] (查看使用部分代码),它失败了(这是预期的)。

那么,如何才能像使用集合一样使用 Foo 的实例,而不必使用 {instance of Foo}.Value
我不确定,但我相信这称为包装器。


我试过的

我试图从 IEnumerable 继承 Foo - 但我以前从未创建过自定义集合类型。因此,在阅读了数小时的教程并盯着 C# 的源代码后,我终于在这里提出了一个关于堆栈溢出的问题。

但是,正如我所说, 无法实施 IEnumerable - 这样做可能仍然是我问题的答案。在那种情况下,请告诉我该怎么做。


这可能是我最长的问题了

这是我最终得到的结果:


Class

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

// In case you really, really want to know,
// this class is called Ticket in my real code and it's used as a Tambola ticket.
public class Foo
{
    protected int?[,] Value { get; set; } = new int?[3 , 9];

    public Foo () => Value = InternalGenerate();

    public static Foo Generate () => new() { Value = InternalGenerate() };
    protected static int?[,] InternalGenerate ()
    {
        // Generate a random instance of this class.
    }

    public IEnumerator<int?> GetEnumerator () => (IEnumerator<int?>) Value.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator () => Value.GetEnumerator();


    public int? this[int r , int c]
    {
        get => Value [r , c];
        private set => Value [r , c] = value;
    }

    public const rowCount = 3;
    public const columnCount = 9;
}

用法

class Program
{
    static void Main ()
    {
        Bar( new() );
    }

    static void Bar ( Foo foo )
    {
        int?[] array = foo.Cast<int?>().ToArray(); // convert foo to 1d array.

        //get "corners" of foo
        int topLeft = (int) array.First(e => e.HasValue);
        int topRight(int) array.Take(columnCount).Last(e => e.HasValue);
        int bottomLeft = (int) array.Skip(( rowCount - 1 ) * columnCount).First(e => e.HasValue);
        int bottomRight = (int) array.Last(e => e.HasValue);
    }
}

实际上您的代码中还有一些错误或问题:

  • 一些语法错误(缺少分号)
  • rowCount 和 columnCount 不一定反映您的 'Valye' 数组的实际值。
  • public IEnumerator<int?> GetEnumerator () => (IEnumerator<int?>) Value.GetEnumerator(); 将抛出 'InvalidCast' 运行时异常。

下面的代码解决了这些问题:

public class Foo: IEnumerable<int?>
{
   public int?[,] Value { get; private set; } = new int?[3, 9];

   public int? this[int x, int y]
   {
       get => Value[x, y];
       set => Value[x, y] = value;
   }

   public Foo() => Value = InternalGenerate();

   public static Foo Generate() => new() { Value = InternalGenerate() };
   
   protected static int?[,] InternalGenerate()
   {
      // Generate a random instance of this class.
      return new int?[3, 9]
        {
            {1,2,3,4,5,6,7,8,9 },
            {9,8,null,6,5,4,3,2,1 },
            {1,2,3,4,5,6,7,8,9 }
        };
   }

   public int GetLength(int dimension) => Value.GetLength(dimension);

   public IEnumerator<int?> GetEnumerator()
   {
       foreach (var item in Value)
       {
           yield return item;
       }
   }

   IEnumerator IEnumerable.GetEnumerator()
   {
     return Value.GetEnumerator();
   }

}

然后你可以实例化一个Foo类型的变量(不是int?[]),想怎么用就怎么用:

    static void Bar(Foo foo)
    {
        int width = foo.GetLength(1);
        int height = foo.GetLength(0);
        var array = foo; // convert foo to 1d array.

        // get "corners" of foo
        int tl = (int)array.First(e => e.HasValue);
        int fl = (int)array.Take(width).Last(e => e.HasValue);
        int lf = (int)array.Skip((height - 1) * width).First(e => e.HasValue);
        int ll = (int)array.Last(e => e.HasValue);
        // this code comes from
        // whosebug.com/questions/68661235/get-corners-of-a-2d-array
    }

如果您希望能够将 Foo 转换为 int?[],您还需要实现转换操作(如果确实有必要的话)。