跨度和二维数组

Span and two dimensional Arrays

是否可以将新的 System.Memory Span struct 与二维数据数组一起使用?

double[,] testMulti = 
    {
        { 1, 2, 3, 4 },
        { 5, 6, 7, 8 },
        { 9, 9.5f, 10, 11 },
        { 12, 13, 14.3f, 15 }
    };

double[] testArray = { 1, 2, 3, 4 };
string testString = "Hellow world";

testMulti.AsSpan(); // Compile error
testArray.AsSpan();
testString.AsSpan();

虽然 testArray 和 testString 有 AsSpan 扩展,但 testMulti 没有这样的扩展。

Span 的设计是否仅限于处理一维数据数组?
我还没有找到使用 Span 处理 testMulti 数组的明显方法。

您可以使用非托管内存创建 Span。这将允许您不加选择地切片和切块

unsafe
{
    Span<T> something = new Span<T>(pointerToarray, someLength); 
}

完整演示

unsafe public static void Main(string[] args)
{
   double[,] doubles =  {
         { 1, 2, 3, 4 },
         { 5, 6, 7, 8 },
         { 9, 9.5f, 10, 11 },
         { 12, 13, 14.3f, 15 }
      };

   var length = doubles.GetLength(0) * doubles.GetLength(1);

   fixed (double* p = doubles)
   {
      var span = new Span<double>(p, length);
      var slice = span.Slice(6, 5);

      foreach (var item in slice)
         Console.WriteLine(item);
   }
}

输出

7
8
9
9.5
10

其他选项是重新分配到一维数组,接受惩罚并且不要传递-Go

  • BlockCopy
  • 或p/invoke直接memcpy并使用unsafe和指针
  • Cast<T> 例如 multiDimensionalArrayData.Cast<byte>().ToArray()

前 2 个对于大型数组来说性能更高。

所有跨度都是一维的,因为内存是一维的。

您当然可以将各种结构映射到一维内存,但 Span class 不会为您做这件事。但是你可以很容易地自己写一些东西,例如:

public class Span2D<T> where T : struct
{
    protected readonly Span<T> _span;
    protected readonly int _width;
    protected readonly int _height;

    public Span2D(int height, int width)
    {
        T[] array = new T[_height * _width];
        _span = array.AsSpan();
    }

    public T this[int row, int column]
    {
        get
        {
            return _span[row * _height + column];
        }
        set
        {
            _span[row * _height + column] = value;
        }
    }
}

棘手的部分是实现 Slice(),因为二维结构的语义有点模糊。您可能只能按其中一个维度对这种结构进行切片,因为按另一个维度对其进行切片会导致内存不连续。

作为@saruman,我认为这是不可能的。

例如,您需要首先使用 Fast way to convert a two dimensional array to a List ( one dimensional ) or Convert 2 dimensional array 中所示的技术获取新的一维数组。

也许使用锯齿状数组而不是多维数组会更成功。

double[][] testMulti = 
    {
        new double[] { 1, 2, 3, 4 },
        new double[] { 5, 6, 7, 8 },
        new double[] { 9, 9.5f, 10, 11 },
        new double[] { 12, 13, 14.3f, 15 }
    };

Span<double[]> span = testMulti.AsSpan(2, 1);
Span<double> slice = span[0].AsSpan(1, 2);

foreach (double d in slice)
    Console.WriteLine(d);

slice[0] = 10.5f;

Console.Write(string.Join(", ", testMulti[2]));

Console.ReadLine();

输出

9.5
10
9, 10.5, 10, 11

正如 John Wu 已经提到的,跨度是一维的。 您当然可以自己实现 2D 跨度,但 Microsoft 已经为我们做到了。

查看文档 here
您可以找到寻址 nuget 包 here.
该包还提供了一个Memory2D。

 var arr = new int[,] { {1,2,3},{2,2,3},{3,2,3} };
 var spn = arr.AsSapn2D();
 // Now use it similar to a normal span
 // The access is of course a bit different since we are using a 2D data structure.
 Console.WriteLine(spn[0..2,..]);