title: Unsafe slicing a 2D array with ReadOnlySpan<T>
body: <p>On .Net Core 3.1, I have many large 2D arrays where I need to operate on a slice of a single row in the array. The same slice may be used by multiple operations so I would like to perform the slice just once and reuse the slice.</p>
  <p>The sample code below slices an array and then calls 2 functions to operate on the slice.</p>
  <pre><code>public void MyFunc()
  {
      double[,] array = ...;  // populate the array
  
      // select which part of the array to slice, values not important
      int index0 = 0;
      int startIndex1 = 1;
      int sliceLength = 2;
  
      // slice the array
      ReadOnlySpan&lt;double&gt; slice = Slice(array, index0, startIndex1, sliceLength);
  
      // do things with the slice
      DoSomething1(slice);
      DoSomething2(slice);
  }
  
  public unsafe ReadOnlySpan&lt;double&gt; Slice(double[,] array, int index0, int startIndex1, int sliceLength)
  {
      int arrayLength = array.GetLength(0) * array.GetLength(1);
      int arrayStartIndex = index0 * array.GetLength(1) + startIndex1;
      ReadOnlySpan&lt;double&gt; slice;
      fixed (double* arrayPtr = array)
      {
          slice = new ReadOnlySpan&lt;double&gt;(arrayPtr, arrayLength).Slice(arrayStartIndex, sliceLength);
      }
  
      // does it matter if slice is returned inside or outside of the fixed block?
      return slice;
  }
  
  public void DoSomething1(ReadOnlySpan&lt;double&gt; slice)
  {
      ...
  }
  
  public void DoSomething2(ReadOnlySpan&lt;double&gt; slice)
  {
      ...
  }
  </code></pre>
  <p>&quot;Fixed&quot; ensures that the GC won't move &quot;array&quot; while I'm creating &quot;slice&quot;. After creating &quot;slice&quot;, if the GC moves &quot;array&quot;, will it also update &quot;slice&quot; to refer to the new &quot;array&quot; address or will &quot;slice&quot; still reference the old address? In other words, will DoSomething1(...) and DoSomething2(...) always operate on the intended slice of the original array, or could they inadvertently operate on a random block of memory?</p>
  <p>Also, does it matter whether &quot;return slice;&quot; is inside or outside the &quot;fixed&quot; block?</p>
  <p><strong>EDIT</strong>
  With inspiration from <a href="https://stackoverflow.com/a/40589439/13532170">https://stackoverflow.com/a/40589439/13532170</a> I managed write a test to prove V0ldek is correct about GC updating the ReadOnlySpan's address when the parent array is moved.</p>
  <pre><code>public static unsafe void ReadOnlySpanTest()
  {
      // create 2D array
      double[,] array = new double[,] { {1, 2, 3}, {4, 5, 6} };
  
      // parameters to convert 2D array to 1D span
      int arrayLength = array.GetLength(0) * array.GetLength(1);
      int sliceStartIndex = 1;
      int sliceLength = 2;
  
      // create span
      IntPtr arrayAddressBeforeMove;
      ReadOnlySpan&lt;double&gt; spanFromPointer;
      fixed (double* arrayPtr = array)
      {
          arrayAddressBeforeMove = (IntPtr)arrayPtr;
  
          // spanFromPointer should contain { 2, 3 }
          spanFromPointer = new ReadOnlySpan&lt;double&gt;(arrayPtr, arrayLength).Slice(sliceStartIndex, sliceLength);
      }
  
      // trick GC into moving the array
      GC.AddMemoryPressure(10000000);
      GC.Collect();
      GC.RemoveMemoryPressure(10000000);
  
      // check array address and span contents again
      IntPtr arrayAddressAfterMove;
      fixed (double* arrayPtr = array)
      {
          // arrayAddressAfterMove should be different from arrayAddressBeforeMove
          arrayAddressAfterMove = (IntPtr) arrayPtr;
  
          // spanFromPointer should still contain { 2, 3 }
      }
  }
  </code></pre>
  <p>Stepping over ReadOnlySpanTest() in the debugger, I can see that arrayAddressAfterMove != arrayAddressBeforeMove, indicating that GC did move my array. I can also see that spanFromPointer contains { 2, 3 } both before and after the array was moved. So doesn't matter that the ReadOnlySpan was created with a &quot;fixed&quot; block, it can still be safely used after leaving the &quot;fixed&quot; block.</p>