Everything about Span: explore new NET star: 1 what is Span < T >?

Everything about Span: explore new NET star


Imagine that it is processing data in memory. You will want to publish an array, get an array parameter, and provide the implementation of operation T [] on the array. This is especially good if the caller can get the array and wants to sort the entire array. However, if the caller only wants to sort the parts of the array, you may provide an overloaded implementation supported by offset and count parameters. But what if you want to support data in memory that is not an array, for example, but from native code? Or is it the data on the stack, you only have the pointer and length to it? How can you develop your sorting method to manipulate such arbitrary memory areas, and still deal with the whole array or a subset of the array as well? Also consider handling managed arrays as well as unmanaged arrays?

Or let's look at another example. You are implementing a system Processing of string. For example, it is a special parsing method. You want to get a string parameter and then provide an implementation for processing strings. But what if you want to support processing subsets of the string? String.Substring() method can be used to extract a part of the string of interest, but it will involve expensive operations, resulting in string allocation and memory replication. You can also do this, as in the array example, through an offset and count parameter. But what if the caller doesn't get this string, but gets a char [] array? Or does the caller get the pointer char *? Or use stack space by calling stackalloc? Or is it the result of calling native code? How can you develop your parsing method without forcing the caller to allocate or copy any memory? And still consistently handle various input types, such as string, char [] and char *?

In both scenarios, you may be able to use unsafe code and pointers to provide input that accepts pointers and lengths. However, it was lost NET opens the door to problems, such as buffer overflow, access conflict and so on NET developers have past problems. It also introduces additional performance penalties, such as the need to pin managed objects during processing so that the pointers you get remain valid. Pointers are not always available based on the underlying data types.

The answer to this puzzle is span < T >

What is span < T >?

System. Span < T > is from NET core. It supports the representation of any continuous interval in memory, whether the memory belongs to a managed object, is obtained through the interoperability of native code, or is allocated on the stack. Although this also provides high-performance secure access similar to array operations.

For example, you can create span < T > from an array:

var arr = new byte[10];
Span<byte> bytes = arr; // Implicit cast from T[] to Span<T>

From here on, you can simply and efficiently use the Slice() overload method of Span to create a Span to represent / point to a subset of the array. Through it, you can read and write the relevant parts of the source array through subscripts.

Span<byte> slicedBytes = bytes.Slice(start: 5, length: 2);
slicedBytes[0] = 42;
slicedBytes[1] = 43;
Assert.Equal(42, slicedBytes[0]);
Assert.Equal(43, slicedBytes[1]);
Assert.Equal(arr[5], slicedBytes[0]);
Assert.Equal(arr[6], slicedBytes[1]);
slicedBytes[2] = 44; // Throws IndexOutOfRangeException
bytes[2] = 45; // OK
Assert.Equal(arr[2], bytes[2]);
Assert.Equal(45, arr[2]);

As mentioned earlier, Span also provides a way to access subsets of arrays. It can also be used to point to data on the stack. For example:

Span<byte> bytes = stackalloc byte[2]; // Using C# 7.2 stackalloc support for spans
bytes[0] = 42;
bytes[1] = 43;
Assert.Equal(42, bytes[0]);
Assert.Equal(43, bytes[1]);
bytes[2] = 44; // throws IndexOutOfRangeException

More conveniently, it can be used to point to any pointer and length, such as memory allocated through the local heap, such as:

IntPtr ptr = Marshal.AllocHGlobal(1);
  Span<byte> bytes;
  unsafe { bytes = new Span<byte>((byte*)ptr, 1); }
  bytes[0] = 42;
  Assert.Equal(42, bytes[0]);
  Assert.Equal(Marshal.ReadByte(ptr), bytes[0]);
  bytes[1] = 43; // Throws IndexOutOfRangeException
finally { Marshal.FreeHGlobal(ptr); }

The indexer of span < T > relies on the C # feature introduced from C# 7.0 called ref return. The ref return type is defined using the indexer Ref. It provides a syntax similar to an indexed array that returns a reference to the actual storage location rather than a copy of the memory in that location.

public ref T this[int index] { get { ... } }

Through this example, the impact of the ref returning indexer is obvious. For example, compared with the List indexer, it is not ref returning. Here is an example:

struct MutableStruct { public int Value; }
Span<MutableStruct> spanOfStructs = new MutableStruct[1];
spanOfStructs[0].Value = 42;
Assert.Equal(42, spanOfStructs[0].Value);
var listOfStructs = new List<MutableStruct> { new MutableStruct() };
listOfStructs[0].Value = 42; // Error CS1612: the return value is not a variable

A variant of span < T > is called system Readonlyspan < T >, which supports read-only access. This type is similar to span < T > except for the ref readonly T feature introduced in C#7.2 instead of ref T. This allows it to handle immutable data types, such as system String. Readonlyspan < T > enables very efficient processing of string slices without allocating or copying memory, such as:

string str = "hello, world";
string worldString = str.Substring(startIndex: 7, length: 5); // Allocates
ReadOnlySpan<char> worldSpan =
  str.AsSpan().Slice(start: 7, length: 5); // No allocation
Assert.Equal('w', worldSpan[0]);
worldSpan[0] = 'a'; // Error CS0200: indexer cannot be assigned to

In addition to these features already introduced, Span offers a variety of advantages. For example, Span supports type conversion symbols, which means that you can force a Span < byte > to Span < int > (where the 0 subscript of Span < int > is mapped to the first 4 bytes of Span < byte >). In this way, if you read the bytes buffer, you can safely and efficiently pass it to a set of bytes, such as int type.

Tags: .NET

Posted by creativeimpact on Fri, 06 May 2022 13:09:10 +0300