如何使一个类在 C# 中用作二维数组?

Posted

技术标签:

【中文标题】如何使一个类在 C# 中用作二维数组?【英文标题】:How do I make a class usable as a 2d array in c#? 【发布时间】:2021-10-12 22:06:11 【问题描述】:

代码

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
        // ***.com/questions/68661235/get-corners-of-a-2d-array
    

此代码将失败。


问题

我有一个名为Foo 的课程。 它的实际值 应该int? 类型的二维数组 - 但是,当我尝试使用 Foo 的实例作为 int?[,] (查看代码的使用部分)时,它失败了(其中预计)。

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


我尝试过的

我尝试从IEnumerable 继承Foo - 但我之前从未创建过自定义集合类型。所以,经过几个小时的阅读教程和盯着 C# 的源代码,我终于求助于这里提出了一个关于堆栈溢出的问题。

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


这可能是我最长的问题了

【问题讨论】:

从现有实现继承要容易得多,而不必自己实现接口。虽然它不是一个真正的多维集合,但您可以尝试:public class Foo : List&lt;List&lt;int&gt;&gt; 好吧,您可以添加自己的 GetLength 方法和自己的索引器 (docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…) - 很难知道这是否就是您所需要的全部。 @MikalSchachtJensen:通过迭代器实现IEnumerable&lt;T&gt; 非常简单;根据我的经验,从List&lt;T&gt; 派生通常不是一个好主意。 (例如,它经常暴露你不想要的操作。) 您使用 Cast 的用例可以使用来自 Value 的非泛型 Enumerator 进行处理。然而,这不是很有效,使用拳击。因此,请让我们相信一个更好更现实的用例。 含义:扩展// do some stuff with array. 【参考方案1】:

其实你的代码还是有一些错误或者问题的:

一些语法错误(缺少分号) rowCount 和 columnCount 不一定反映“Valye”数组的实际值。 public IEnumerator&lt;int?&gt; GetEnumerator () =&gt; (IEnumerator&lt;int?&gt;) 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
        // ***.com/questions/68661235/get-corners-of-a-2d-array
    

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

【讨论】:

【参考方案2】:

这是我最终得到的结果:


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);
    

【讨论】:

以上是关于如何使一个类在 C# 中用作二维数组?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 c# 二维数组转换为 JSON 对象?

如何将二维数组作为函数参数

如何将二维数组从 C# 传递到 C++?

C# CS结构中 2005 如何将二维数组转化成DataSet

如何在 C# 中对二维(矩形)数组进行排序?

如何在 C# 中为 Matrix 类初始化二维数组