Span<T>.Enumerator Struct

Definition

Provides an enumerator for the elements of a Span<T>.

public: value class Span<T>::Enumerator
public ref struct Span<T>.Enumerator
type Span<'T>.Enumerator = struct
Public Structure Span(Of T).Enumerator

Type Parameters

T
Inheritance
Span<T>.Enumerator

Remarks

The C# foreach of the C# language and the For Each...Next construct in Visual Basic hides the complexity of enumerators. Instead of directly manipulating the enumerator, using foreach or For Each...Next is recommended.

Initially, the enumerator is positioned before the first element in the Span<T>. At this position, Current is undefined. You must call MoveNext to advance the enumerator to the first item in the Span<T> before reading the value of Current.

Current returns the same value until MoveNext is called. MoveNext sets Current to the next item in the Span<T>.

If MoveNext passes the end of the Span<T>, MoveNext returns false. When the enumerator is at this state, subsequent calls to MoveNext also return false and Current is undefined. You cannot set Current to the first item in the Span<T> again; you must create a new enumerator instance instead.

The enumerator does not have exclusive access to the Span<T>. In addition, the underlying data on which the span is based can also be modified. Therefore, enumerating through a span is intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you must implement your own synchronization. For example, the following code has a race condition. It does not ensure that the span will be enumerated before the ClearContents method executes. As a result, the underlying array is cleared during enumeration of the span:

using System;
using System.Threading.Tasks;

class Program
{
    private static readonly byte[] _array = new byte[5];

    static void Main()
    {
        new Random(42).NextBytes(_array);
        Span<byte> span = _array;

        Task.Run( () => ClearContents() );

       EnumerateSpan(span);
    }

    public static void ClearContents()
    {
        Task.Delay(20).Wait();
        lock (_array)
        {
           Array.Clear(_array, 0, _array.Length);
        }
    }

    public static void EnumerateSpan(Span<byte> span)
    {
        foreach (byte element in span)
        {
            Console.WriteLine(element);
            Task.Delay(10).Wait();
        }
    }
}
// The example displays output like the following:
//     62
//     23
//     186
//     0
//     0
module Program

open System
open System.Threading.Tasks

let array = Array.zeroCreate<byte> 5

let clearContents () =
    Task.Delay(20).Wait()
    lock array (fun () -> 
        Array.Clear(array, 0, array.Length) )

let enumerateSpan (span: Span<byte>) =
    for element in span do
        printfn $"{element}"
        Task.Delay(10).Wait()

[<EntryPoint>]
let main _ =
    Random(42).NextBytes array
    printfn "%A" array
    let span: Span<byte> = array

    Task.Run clearContents |> ignore

    enumerateSpan span
    
    0

// The example displays output like the following:
//     62
//     23
//     186
//     0
//     0

If you synchronize access to the array before enumerating the span, as the revised version of the EnumerateSpan method does in the following example, the ClearContents method doesn't modify underlying span data during enumeration. Note that the example locks the underlying array on which the span is based.

public static void EnumerateSpan(Span<byte> span)
{
    lock (_array)
    {
        foreach (byte element in span)
        {
            Console.WriteLine(element);
            Task.Delay(10).Wait();
        }
    }
}
// The example displays the following output:
//    62
//    23
//    186
//    150
//    174
let enumerateSpan (span: Span<byte>) =
    // Spans cannot be accessed in closures including in the F# lock function.
    // Monitor.Enter and Monitor.Exit are used here directly.
    Monitor.Enter array
    try
        for element in span do
            printfn $"{element}"
            Task.Delay(10).Wait()
    finally
        Monitor.Exit array
// The example displays the following output:
//    62
//    23
//    186
//    150
//    174

Unlike some other enumerator structures in .NET, the Span<T>.Enumerator:

  • Does not implement the IEnumerator or IEnumerator<T> interface. This is because Span<T>.Enumerator is a ref struct.

  • Does not include a Reset method, which can set the enumerator to its initial position before the first element in the span. (The IEnumerator.Reset() method must be implemented as part of the interface, but most implementors either throw an exception or provide no implementation.)

Properties

Current

Gets a reference to the item at the current position of the enumerator.

Methods

MoveNext()

Advances the enumerator to the next item of the Span<T>.

Applies to