C# で生産性を向上させよう～C# 13 アップデート完全ガイド～

# はじめに

2024 年 11 月に .NET 9 とともに C# 13 が正式リリースされる予定です。今回の C# 年度アップデートは、主に `ref struct` を中心に多くの改善が行われ、生産性をさらに向上させる便利な機能もたくさん搭載されています。

この記事では、C# 13 に搭載する予定の機能を紹介していきます。

注意：この時点で C# 13 はまだ正式リリースされていませんので、以下の内容は変更される可能性があります。

# イテレータと非同期メソッドに `ref` と `ref struct`

C# でプログラミングをするとき、結構 `ref` 変数と `Span` などの `ref struct` 型を使ったりしませんか？しかしイテレータと非同期メソッドの中にこれらを使えず、ローカル関数などを使ってイテレータと非同期メソッドで直接 `ref` 変数 `ref struct` 型の使用を回避しないといけないというとっても不便なところがありました。

この欠点が C# 13 で改善され、イテレータと非同期メソッドにも `ref` と `ref struct` が使えるようになりました！

イテレータに `ref` と `ref struct` を使う例：

```csharp
IEnumerable<float> GetFloatNumberFromIntArray(int[] array)
{
    for (int i = 0; i < array.Length; i++)
    {
        Span<int> span = array.AsSpan();
        // 何かの処理を行う...
        ref float v = ref Unsafe.As<int, float>(ref array[i]);
        yield return v;
    }
}
```

非同期メソッドに `ref struct` を使う例：

```csharp
async Task ProcessDataAsync(int[] array)
{
    Span<int> span = array.AsSpan();
    // 何かの処理を行う...
    ref int element = ref span[42];
    element++;
    await Task.Yield();
}
```

機能を示すために不適切かつ意味不明な「何かの処理」を使いましたが、とにかく `ref` と `ref struct` が使用できるようになりました！

ただ、一つ注意すべき点として、`ref` 変数と `ref struct` 型の変数は `yield` と `await` の境界に超えると使えなくなります。例えば以下の例はコンパイルエラーになります。

```cs
async Task ProcessDataAsync(int[] array)
{
    Span<int> span = array.AsSpan();
    // 何かの処理を行う...
    ref int element = ref span[42];
    element++;
    await Task.Yield();
    element++; // エラー：element へのアクセスが await の境界に超えている
}
```

ここまで話しましたが、そもそも `ref` と `ref struct`ってなにという疑問を抱える方はいらっしゃると思いますので、少し説明していきます。

C# では、`ref` を使って変数の参照を取得できます。これにより、参照を通じて元の変数を変更できます。以下に例を示します：

```csharp
void Swap(ref int a, ref int b) // ref で参照を表す
{
    int temp = a;
    a = b;
    b = temp; // ここまで a と b が交換された
}

int x = 1;
int y = 2;
Swap(ref x, ref y); // x と y の参照を取り、Swap を呼び出して x と y を交換する
```

一方、`ref struct` はスタック上にのみ存在できる値型を定義するためのものです。これは、ガベージコレクションのオーバーヘッドを避けるために使用されます。ただし、`ref struct` はスタック上にしか存在できないため、C# 13 以前にはイテレータや非同期メソッドなどのところに使用できません。

ちなみに、`ref struct` に `ref` が付いている理由は、`ref struct` のインスタンスがスタック上でのみ存在でき、そのライフタイムが従うルールは `ref` 変数と同じであるためです。


# `allows ref struct` ジェネリック制約

従来では、`ref struct` はジェネリック型引数として使えないので、コードの再利用性を考慮してジェネリックを導入したものの、結局`ref struct` が使用できなくなり、`Span` や `ReadOnlySpan` のために同じ処理を再度書かなければならないという問題がありました。

これは結構ややこしいと思いませんでしょうか？

C# 13 では、ジェネリック型にも `ref struct` が使えようになりました：

```csharp
using System;
using System.Numerics;

Process([1, 2, 3, 4], Sum); // 10
Process([1, 2, 3, 4], Multiply); // 24

T Process<T>(ReadOnlySpan<T> span, Func<ReadOnlySpan<T>, T> method)
{
    return method(span);
}

T Sum<T>(ReadOnlySpan<T> span) where T : INumberBase<T>
{
    T result = T.Zero;
    foreach (T value in span)
    {
        result += value;
    }
    return result;
}

T Multiply<T>(ReadOnlySpan<T> span) where T : INumberBase<T>
{
    T result = T.One;
    foreach (T value in span)
    {
        result *= value;
    }
    return result;
}
```

そもそもなぜ `ReadOnlySpan<T>` ような `ref struct` 型が `Func` の型引数として使えるんでしょうか？これを調査するために .NET の[ソースコード](https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Function.cs,9)を見ると、`Func` 型のジェネリック引数が以下のように定義されています：

```csharp
public delegate TResult Func<in T, out TResult>(T arg)
    where T : allows ref struct
    where TResult : allows ref struct;
```

なるほど、ジェネリック引数に `allow ref struct` という制約を追加したら、その引数に `ref struct` 型を渡すことができるようになります。

そうだとしたらこれは確かに便利な機能ですね。

# `ref struct` にもインターフェースを実装できる

C# 13 では、`ref struct` にインターフェースの実装が可能になりました。

この機能と `allows ref struct` と組み合わせてうまく利用すれば、ジェネリック型を介して参照も渡せますね：

```csharp
using System;
using System.Numerics;

int a = 10;
// Ref<int> で a の参照を保存する
Ref<int> aRef = new Ref<int>(ref a);
// Ref<int> を渡す
Increase<Ref<int>, int>(aRef);
Console.WriteLine(a); // 11

void Increase<T, U>(T data) where T : IRef<U>, allows ref struct where U : INumberBase<U>
{
    ref U value = ref data.GetRef();
    value++;
}

interface IRef<T>
{
    ref T GetRef();
}

// Ref<T> という ref struct にインターフェースを実装する
ref struct Ref<T> : IRef<T>
{
    private ref T _value;

    public Ref(ref T value)
    {
        _value = ref value;
    }

    public ref T GetRef()
    {
        return ref _value;
    }
}
```

これで `ref struct` に関するコードを書くのがかなり楽になりましたね。`ref struct` で実装された列挙型にも `IEnumerator` などのインターフェースを実装できるようになりましたね。

# コレクション型や `Span` にも `params`

従来では、`params` は配列型にしか使えないのですが、C# 13 から他のコレクション型や `Span` にも使えるようになりました。

`params` とは、メソッドが呼び出される際に任意の数の引数を直接指定できるようにする機能です。

例えば、

```csharp
Test(1, 2, 3, 4, 5, 6);
void Test(params int[] values) { }
```

上記のように、任意の数の `int` 引数を直接指定できます。

C# 13 から、配列型以外に、他のコレクション型、`Span`、`ReadOnlySpan` 型やコレクションに関するインターフェースにも `params` を付けるようになりました：

```csharp
Test(1, 2, 3, 4, 5, 6);
void Test(params ReadOnlySpan<int> values) { }

// あるいは
Test(1, 2, 3, 4, 5, 6);
void Test(params List<int> values) { }

// インターフェースもOK
Test(1, 2, 3, 4, 5, 6);
void Test(params IEnumerable<int> values) { }
```

これも便利です！

# `field` キーワード

C# のプロパティを実装するとき、以下のようにいちいちフィールドを定義しないといけないときはよくありましたね...

```csharp
partial class ViewModel : INotifyPropertyChanged
{
    // フィールドを定義する
    private int _myProperty;

    public int MyProperty
    {
        get => _myProperty;
        set
        {
            if (_myProperty != value)
            {
                _myProperty = value;
                OnPropertyChanged();
            }
        }
    }
}
```

そこで、C# 13 から `field` キーワードが助けになります！

```csharp
partial class ViewModel : INotifyPropertyChanged
{
    public int MyProperty
    {
        // field を使うだけで十分
        get => field;
        set
        {
            if (field != value)
            {
                field = value;
                OnPropertyChanged();
            }
        }
    }
}
```

自分でフィールドを定義する必要がなくなり、`field` キーワードを使えばフィールドが自動生成されます。

これもとても便利ですね！

# 部分プロパティ

C# を書くときよくあるある問題その一つ：プロパティに `partial` 修飾子を付けられません。

いきなり `partial` を説明すると意外とわからないかもしれませんので、既存の `partial` メソッドや`partial` クラスについても説明していきます。

C# では、クラスやメソッドに `partial` を付くことで、宣言と実装を別々で行うことができます。また、クラスの各部分を分散することもできます。一番の用途としては、ソースジェネレーターなどの自動生成ツールに、何を生成するのかを指定するときに使います。

例えば：

```csharp
partial class ViewModel
{
    // ここはメソッドを宣言するだけ、実装の部分はツールで自動生成される
    partial void OnPropertyChanged(string propertyName);
}
```

そして自動生成ツールが以下のようなコードを生成します：

```csharp
partial class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    partial void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new(propertyName));
    }
}
```

開発者はただ `OnPropertyChanged` を宣言するだけで十分、その実装がすべて自動生成されますので、開発者の手間が省けます。

C# 13 から、プロパティも `partial` を対応します：

```csharp
partial class ViewModel
{
    // 部分プロパティを宣言する
    public partial int MyProperty { get; set; }
}

partial class ViewModel
{
    // 部分プロパティの実装
    public partial int MyProperty
    {
        get
        {
            // ...
        }
        set
        {
            // ...
        }
    }
}
```

これでプロパティもツールによって自動生成できるようになりましたね。

# ロックオブジェクト

`lock` という機能はご存知の通り、モニターを通じてスレッド同期に使用する機能です。

```csharp
object lockObject = new object();
lock (lockObject)
{
    // 排他的なスコープ
}
```

しかしこの機能は、オーバーヘッドが大きく、パフォーマンスに影響を与えます。

これを解決するために C# 13 はロックオブジェクトが実装されました。この機能を使用するには、`System.Threading.Lock` でロックされるオブジェクトを入れ替えれば十分です：

```csharp
using System.Threading;

Lock lockObject = new Lock();
lock (lockObject)
{
    // 排他的なスコープ
}
```

これで簡単にパフォーマンスを向上することができますね。

# 初期化子での末尾からのインデックス

インデックス演算子 `^` は、集合の末尾に対する相対位置を示すために使用できます。C# 13 から初期化子でも機能します：

```csharp
var x = new Numbers
{
    Values = 
    {
        [1] = 111,
        [^1] = 999 // ^1 は末尾から一番目の要素
    }
    // x.Values[1] は 111
    // x.Values[9] は 999、なぜなら Values[9] は最後の要素だからです
};

class Numbers
{
    public int[] Values { get; set; } = new int[10];
}
```

# エスケープ文字

Unicode 文字列に `\u001b` と `\x1b` の代わりに `\e` が使えようになりました。`\u001b`、`\x1b`、そして `\e` はすべてエスケープ文字を表します。これらは通常、制御文字を表現するために使用されます。

- `\u001b` は Unicode エスケープシーケンスを表し、`\u` の後に続く 4 桁の 16 進数は Unicode コードポイントを表します
- `\x1b` は 16 進エスケープシーケンスを表し、\x の後に続く 2 桁の 16 進数は ASCII コードを表します
- `\e` はエスケープ文字そのものを表します

なぜ `\e` の使用がおすすめというと、16 進数における解釈の混乱を避けるためです。

例えば、`\x1b` に `3` がついてるの場合は、`\x1b3` になってしまい、`\x1b` と `3` の間に明確な区切りがないため、`\x1b` と `3` を別々に解釈するべきか、それとも一緒に解釈するべきかが不明確になります。

代わりに `\e` を使えば、解釈の混乱を避けられます。

# そのほか

上記の機能以外、メソッドグループでの自然型とメソッドオーバーロードにおける優先順位に関してもいくつの改善がありますが、この記事で省略します。さらに詳しく知りたい場合はドキュメントまで参照してください。

# おわりに

C# が年々進化し続けていることが改めて実感されますね。今回 C# 13 のアップデートは、筆者にとってかなり実用かつ便利な機能がいっぱい実装されて、大変うれしいと思います。

.NET 9 と C# 13 の正式リリースを楽しみしています～

皆様はいかがでしょうか？