【问题标题】:How do I make a class usable as a 2d array in c#?如何使一个类在 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
        // stackoverflow.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.

标签: c# arrays .net multidimensional-array c#-9.0


【解决方案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
        // stackoverflow.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);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-14
      • 1970-01-01
      • 2013-06-29
      • 1970-01-01
      • 2011-01-26
      • 2021-07-22
      相关资源
      最近更新 更多