諸事情で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
が式として書けるようになりました。
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番目の要素を取り出す」ことができるようになりました。
public void Method(List<int> list)
{
var trim = list[1..^1];
}
尚、[i..^j]
というように後ろ側の添字に^
をつけることで、「j個取り除く」ことができます。
つまり、[1..^1]
と書くと両端を1個ずつ取り除くことができます。
using 変数宣言
using
が「スコープ」に対してではなく、「変数」に対して行えるようになりました。
その変数のスコープを脱出する際にDispose()
されるようです。
public void Method()
{
using var a = new DisposableObject();
}
Null合体代入
??=
のことです。
キャッシュに使用できます。
private Dictionary<int, string> _dict;
public void Method(int index, string message)
{
_dict[index] ??= message;
}
@$の順序
文字列補完式である@
と$
は今までは併用する場合「$@
」しか認められていませんでしたが、「@$
」でも許容されるようになりました。
アクセサーに対するObsolete
指定
片方のアクセサーにのみObsolete
指定ができるようになりました。
public int Position
{
[Obsolete] set;
get;
}
C# 9.0
レコード型
OOP1においてはそのクラスが「どう振る舞うか」が重要であり、「どうであるか(実装)」は重要ではないのですが、時としてデータそのものが必要となるため2、「レコード型」というデータに使われるような型ができました。
using System;
record Player(string id, string Name);
何が嬉しいかというと、データを扱うのであれば比較を行う時があると思います。
そういうときは、データクラスでEquals()
をoverrideして一つずつフィールドを比較したり、GetHashCode()
をオーバーライドする必要がありました。
しかしこのレコード型では「Equals」「GetHashCode」「==」は勝手に生成してくれるようなので、その礼儀的なコードを書く必要はなくなりました。
initアクセサー
set
の代わりに「init
」と記述することで、初期化子までは書き換え可能で、尚且つそれ以降においては書き換えが不可能なプロパティが作成できるようになりました。
class Clazz
{
public int Value { init; get; }
}
トップレベルステートメント
トップレベルに文が書けます。
using System;
Console.WriteLine("Hi there, This is Top Level.");
int Add(int x, int y) => x + y;
プロジェクト全体で1ファイルのみに書けるようです。
パターンマッチングのパターンの追加
not, and, or
が使えます。
public bool Method(int index) => index is 0 or 9;
public bool IsNotString(object s) => s is not string;
new()型推論
型が指定されているフィールドや変数に対して、型推論が効くようになりました。
public void Method()
{
SomeClass s = new ();
}
Dictionary<int, List<(double x, double y)>>
など型名が長い型をフィールドとして持ちたいときに、初期化子としてnew ()を用いることで更に短くなるようになりました。
共変戻り値
virtual
指定したメソッドの戻り値がサブクラスでサブクラスに変更できるようになりました。
SubClazz
はBaseClazz
に安全に変換(アップキャスト)できるので、妥当な仕様ではあります。
class BaseClazz
{
virtual BaseClazz Clone() => new BaseClazz();
}
class SubClazz : BaseClazz
{
override SubClazz Clone() => new SubClazz();
}
native int (※要unsafe)
nint
とnuint
というキーワードを使用することで、動作環境で一番高速に扱える整数を指定することができるようになりました。
unsafe
{
nint x = 0b_1;
}
ラムダ式での破棄
ラムダ式で破棄ができるようになりました。後方互換性を保つために、_
が2回以上登場したときに限り有効です。
(_, _) => { };
C# 10.0
C# 10.0
以降はUnityではまだ使えません。
値型record
record型が値型に指定できるようになりました。
using System;
record struct Position(int X, int Y);
ファイルスコープ名前空間
今までのようなスコープで覆うnamespaceの指定の仕方ではなく、文として名前空間を指定できるようになりました。
namespace Assets;
class Clazz
{
}
全域using
プロジェクト全体に影響を及ぼすusing
が使用できるようになりました。
global using System;
ちなみにこれをUnityでやると完全型名指定していない場合System.Random
とUnityEngine.Random
があいまいになります。
分解宣言と分解代入の拡張
このような書き方も許容されるようになりました。
public void Method()
{
int x;
(x, var y) = (1, 2);
}
C# 11.0
requiredメンバー
プロパティとフィールドに対して、required
修飾子が追加されました。
そのクラスを生成する場合、初期化子で値を指定することが必須になります。
public class Clazz
{
public required string ID { init; get; }
}
生文字列
"""
を使用することで、生文字列が使用できるようになりました。
\ はそのまま \ として扱われ、{}もそのまま{}として扱われます。
以下のコードでのID_Format
は「ID: {ID}
」という文字列になります。
public string ID_Format = """ID: {ID}""";
そして、補完を行いたい場合は$$""""""
と{{}}
を使用することで可能です3。
public string ID_Format = $$"""{"ID": "{{FieldID}}"}""";
生文字列中に"""
を使いたい場合は、"を1個増やせば"""
がそのままの文字として扱われるようになります。
つまり"""" """ """"
と書けば「"""
」になりますし、
""""" """" """""
と書けば「""""
」になります。
リストに対するパターンマッチング
Listや配列に対してパターンマッチングできるようになりました。
public bool IsEmptyOrSingle(List<int> list) => list switch
{
[] or [_] => true,
_ => false,
};
[]の中に入るのはパターンであって、IndexやSizeではないことに注意してください。
つまり、リストに対するパターンマッチングで[1, ..]
と書くと「要素が1個のリスト」ではなく「最初の要素が1であるリスト」になります。
常に符号なしの右シフト
>>>
が追加されました。
シフト演算子の右オペランドの制限撤廃
今までシフト演算子は右側がintでなければいけませんでしたが、int以外の型が指定できるようになりました。
public static Clazz operator <<(Clazz a, Clazz i) => default;
Generic Math
実装による追加のようです。
悪用厳禁4。
fileローカル型
同一ファイル内からのみ使用できるような修飾子ができました。
file static class Extension
{
}
file enum SomeEnum
{
None,
}
ただし、トップレベルの場所でしかfileは指定できません。
文字列補間中の改行
@$ではできていたので、どちらかと言えばバグ修正に近いです。
public static string ID = $"id:{
someExternalID
}";
文脈キーワードの型名指定不可化
以下のようにC# 11.0
で追加された文脈キーワードを型名に指定するとエラーになります。
今までの文脈キーワードは警告は出るこそすれ、エラーにはなりませんでした。
class file { }