C#7.0浦島太郎によるC#11までのまとめ

諸事情で`C# 7.0`を扱っていたときから時間は経ち、`C# 11.0`になりました。

ということで、個人的に有益だと感じた`C# 8.0`から`C# 11.0`の機能を書いておこうと思います。
個人的にUnity等で使用したい部分に絞っているので、ここに載っていない更新などがあるかもしれないです。公式文書を読んで下さい。

# C# 8.0
## Null許容参照型
今までは```string```のような参照型は、必然的にNull許容でした。
つまり参照型であれば`NullReferenceException`が発生する可能性が常につきまとい、先人たちと私は「`Null Object`」というデザインパターンによってそれを防いできました。
(例えば`string`であれば「`string.Empty`」が妥当でしょう)

ですが、`C# 8.0`にてopt-inで型`T`をデフォルトでNull非許容にするオプションができました。
(Nullチェックをしていないと警告が出ます)

この機能は`pragma`で
```
#nullable enable
```
と書くと有効になります。
そのファイル内でNull許容(デフォルト)に一時的に戻したい場合は、「`T?`」と書くとNull許容になります。

## switch式
`switch`が式として書けるようになりました。
```csharp:switch.cs
public int Compare(int? x, int? y) => (x, y) switch {
    (int i, int j) => i.CompareTo(j),
    ({ }, null) => 1,
    (null, { }) => -1,
    (null, null) => 0
};
```

## 範囲アクセス
配列やListなどの添字を使ってアクセスするクラスに対して、「`i..j`」という指定を行うと「i番目からj番目の要素を取り出す」ことができるようになりました。
```csharp
public void Method(List<int> list)
{
    var trim = list[1..^1];
}
```
尚、`[i..^j]`というように後ろ側の添字に`^`をつけることで、「j個取り除く」ことができます。
つまり、`[1..^1]`と書くと両端を1個ずつ取り除くことができます。

## using 変数宣言
`using`が「スコープ」に対してではなく、「変数」に対して行えるようになりました。
その変数のスコープを脱出する際に`Dispose()`されるようです。
```csharp:usingcs
public void Method()
{
    using var a = new DisposableObject();
}
```

## Null合体代入
`??=`のことです。
キャッシュに使用できます。
```csharp
private Dictionary<int, string> _dict;

public void Method(int index, string message)
{
    _dict[index] ??= message;
}
```

## @$の順序
文字列補完式である`@`と`$`は今までは併用する場合「`$@`」しか認められていませんでしたが、「`@$`」でも許容されるようになりました。

## アクセサーに対する`Obsolete`指定
片方のアクセサーにのみ`Obsolete`指定ができるようになりました。
```csharp
public int Position
{
    [Obsolete] set;
    get;
}
```

# C# 9.0

## レコード型
OOP[^1]においてはそのクラスが「どう振る舞うか」が重要であり、「どうであるか(実装)」は重要ではないのですが、時としてデータそのものが必要となるため[^2]、「レコード型」というデータに使われるような型ができました。
```csharp:record.cs
using System;

record Player(string id, string Name);
```
何が嬉しいかというと、データを扱うのであれば比較を行う時があると思います。
そういうときは、データクラスで`Equals()`をoverrideして一つずつフィールドを比較したり、`GetHashCode()`をオーバーライドする必要がありました。
しかしこのレコード型では「Equals」「GetHashCode」「==」は勝手に生成してくれるようなので、その礼儀的なコードを書く必要はなくなりました。
[^1]:C#は正確にはOOPではありませんが、骨格がOOPでできているのでそう表現しました
[^2]:後述の通り理由はそれだけではありません

## initアクセサー
`set`の代わりに「`init`」と記述することで、初期化子までは書き換え可能で、尚且つそれ以降においては書き換えが不可能なプロパティが作成できるようになりました。
```csharp:init.cs
class Clazz
{
    public int Value { init; get; }
}
```

## トップレベルステートメント
トップレベルに文が書けます。
```csharp:Top.cs
using System;

Console.WriteLine("Hi there, This is Top Level.");

int Add(int x, int y) => x + y;
```
プロジェクト全体で1ファイルのみに書けるようです。

## パターンマッチングのパターンの追加
`not, and, or`が使えます。
```csharp:Pattern.cs
public bool Method(int index) => index is 0 or 9;
public bool IsNotString(object s) => s is not string;
```

## new()型推論
型が指定されているフィールドや変数に対して、型推論が効くようになりました。
```csharp:New.cs
public void Method()
{
    SomeClass s = new ();
}
```
`Dictionary<int, List<(double x, double y)>>`など型名が長い型をフィールドとして持ちたいときに、初期化子としてnew ()を用いることで更に短くなるようになりました。

## 共変戻り値
`virtual`指定したメソッドの戻り値がサブクラスでサブクラスに変更できるようになりました。
`SubClazz`は`BaseClazz`に安全に変換(アップキャスト)できるので、妥当な仕様ではあります。
```csharp
class BaseClazz
{
    virtual BaseClazz Clone() => new BaseClazz();
}

class SubClazz : BaseClazz
{
    override SubClazz Clone() => new SubClazz();
}
```

## native int (※要unsafe)
`nint`と`nuint`というキーワードを使用することで、動作環境で一番高速に扱える整数を指定することができるようになりました。
```csharp:nint.cs
unsafe
{
    nint x = 0b_1;
}
```

## ラムダ式での破棄
ラムダ式で破棄ができるようになりました。後方互換性を保つために、`_`が2回以上登場したときに限り有効です。
```csharp
(_, _) => { };
```

# C# 10.0
`C# 10.0`以降はUnityではまだ使えません。

## 値型record
record型が値型に指定できるようになりました。
```csharp
using System;

record struct Position(int X, int Y);
```

## ファイルスコープ名前空間
今までのようなスコープで覆うnamespaceの指定の仕方ではなく、文として名前空間を指定できるようになりました。
```csharp
namespace Assets;

class Clazz
{
}
```

## 全域using
プロジェクト全体に影響を及ぼす`using`が使用できるようになりました。
```csharp
global using System;
```
ちなみにこれをUnityでやると完全型名指定していない場合`System.Random`と`UnityEngine.Random`があいまいになります。

## 分解宣言と分解代入の拡張
このような書き方も許容されるようになりました。
```csharp
public void Method()
{
    int x;
    (x, var y) = (1, 2);
}
```

# C# 11.0

## requiredメンバー
プロパティとフィールドに対して、`required`修飾子が追加されました。
そのクラスを生成する場合、初期化子で値を指定することが必須になります。

```csharp:required.cs
public class Clazz
{
    public required string ID { init; get; }
}
```

## 生文字列
`"""`を使用することで、生文字列が使用できるようになりました。
\ はそのまま \ として扱われ、{}もそのまま{}として扱われます。
以下のコードでの`ID_Format`は「`ID: {ID}`」という文字列になります。
```csharp
public string ID_Format = """ID: {ID}""";
```
そして、補完を行いたい場合は`$$""""""`と`{{}}`を使用することで可能です[^3]。
[^3]:当然「"」や「\」はエスケープされないのでこのような記述が可能ですが、Markdownの仕様がまだC#11.0に非対応です。
```csharp
public string ID_Format = $$"""{"ID": "{{FieldID}}"}""";
```
生文字列中に`"""`を使いたい場合は、"を1個増やせば`"""`がそのままの文字として扱われるようになります。
つまり`"""" """ """"`と書けば「`"""`」になりますし、
`""""" """" """""`と書けば「`""""`」になります。

## リストに対するパターンマッチング
Listや配列に対してパターンマッチングできるようになりました。
```csharp
public bool IsEmptyOrSingle(List<int> list) => list switch
{
    [] or [_] => true,
    _ => false,
};
```
[]の中に入るのはパターンであって、IndexやSizeではないことに注意してください。
つまり、リストに対するパターンマッチングで`[1, ..]`と書くと「要素が1個のリスト」ではなく「最初の要素が1であるリスト」になります。

## 常に符号なしの右シフト
`>>>`が追加されました。

## シフト演算子の右オペランドの制限撤廃
今までシフト演算子は右側がintでなければいけませんでしたが、int以外の型が指定できるようになりました。
```csharp
public static Clazz operator <<(Clazz a, Clazz i) => default;
```
`Generic Math`実装による追加のようです。
悪用厳禁[^4]。
[^4]:やり方によってはC++の文法(cout)のようなものが作れたりします。

## fileローカル型
同一ファイル内からのみ使用できるような修飾子ができました。
```csharp
file static class Extension
{
}
file enum SomeEnum
{
    None,
}
```
ただし、トップレベルの場所でしかfileは指定できません。

## 文字列補間中の改行
@$ではできていたので、どちらかと言えばバグ修正に近いです。
```csharp
public static string ID = $"id:{
someExternalID
}";
```

## 文脈キーワードの型名指定不可化
以下のように`C# 11.0`で追加された文脈キーワードを型名に指定するとエラーになります。
今までの文脈キーワードは警告は出るこそすれ、エラーにはなりませんでした。
```csharp
class file {  }
```