12

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

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 数组的明显方法。

4

5 回答 5

9

您可以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

其他选择是重新分配给一维数组,接受惩罚并且不通过

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

对于大型阵列,前 2 个性能更高。

于 2018-10-11T00:55:45.123 回答
2

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

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

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(),因为二维结构的语义有点模棱两可。您可能只能按一个维度对这种结构进行切片,因为按另一个维度切片会导致内存不连续。

于 2018-10-11T01:12:36.737 回答
2

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

看看这里的文档。您可以在此处
找到寻址 nuget 包。 该软件包还提供了一个 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,..]);
于 2021-05-19T11:17:25.663 回答
0

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

例如,您将需要首先使用快速方法中显示的技术来获取一个新的一维数组,以将二维数组转换为列表(一维)转换二维数组

于 2018-10-11T01:14:20.187 回答
0

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

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
于 2018-10-11T01:19:55.723 回答