10yroの開発日記

福岡にある株式会社10yro(トイロ)のエンジニアが書いています

C# Span<T>構造体と配列

C# 7.2以降で追加されたSpan<T>構造体や配列などに追加された演算子についての備忘録です。

Span<T>構造体

Span<T>構造体とは、配列などのデータが並んでいるものから一部を取り出して、値を読み書きするものです。

Span<T>構造体は、ref 構造体という参照型の構造体になっていて、文字列などの連続したメモリ領域を直接参照できるので、バイナリデータなどに対してもC言語ポインターのような感覚で安全に扱うことができます。おまけに高速です。

また、読み取り専用のReadOnlySpan<T>構造体も提供されています。

配列を参照する

// int型の配列
var array = new int[]{ 2022, 1, 23, 4, 0 };

// 2番目から2要素分の参照
var spanData = new Span<int>(array, 1, 2);

foreach (var val in spanData)
{
    Console.Write(val); // 123が出力される
}

spanData[0] = 5; // spanDataの1番目を書き換える

foreach (var val in array)
{
    Console.Write(val); // 202252340が出力される
}

文字列を参照する

以下は、ReadOnlySpan<T>構造体を返す拡張メソッドのAsSpanを使用したサンプルです。
なお、Sliceメソッドは位置と長さを指定してデータを切り出すメソッドです。

// 3文字目から3文字を切り出し
var spanData2 = "XXABCXXX".AsSpan().Slice(2, 3);

foreach (var val in spanData2)
{
    Console.Write(val);   // "ABC" が出力される
}

C#のstring型は変更できないオブジェクトなので、AsSpanメソッドは読み取り専用のReadOnlySpanを返却します。
よって、上記のspanData2は、ReadOnlySpanなので書き換えできません。
なお、配列に対してAsSpanメソッドを使う場合は、Spanを返します。

インデックスと範囲の演算子「^」「..」

C# 8.0で追加された演算子^..を使うと、範囲を指定する構文がシンプルに記述できるようになりました。

Span<T>ReadOnlySpan<T>に範囲を指定する際にも使用できます。

例)
 ^N: 末尾からN番目

 N..^M: N+1からM番目。※先頭の位置は0から数える。

var array = new int[]{ 2022, 1, 23, 4, 0 };

// 末尾から3番目
Console.WriteLine(array [^3]); // 23が出力される

// 2~4番目
var span = array.AsSpan()[1..4];

foreach (var val in span)
{
    Console.Write(val); // 1234が出力される
}