Open7

Modern C# 2024 Edition

ピン留めされたアイテム
いぬいぬいぬいぬ
  • 「モダンC#2024」の定義

    • 現在(2024)から5年以内にC#に追加された記法・文法・APIを使うもの
  • モダンC#2024に含まれないもの(モダンC#2024では「基本」になるもの)

    • LINQ、ラムダ式 : C#3.0 2007
    • async/await : C#5.0 2012
    • null 条件演算子 : C#6.0 2015
    • タプル記法 / ValueTuple: C#7.0 2017
    • Span<T> : C#7.2 2017
いぬいぬいぬいぬ

パターンマッチングと対応構文

  • パターンマッチングはC#7.0から入り始め、C#11.0まで順に追加されている
    • 一部今回の厳密な「モダンC#」基準が外れるが、まとめて説明
  • それぞれのパターンに対応する記法・文法もC#に追加され、影響はC#全般に渡る

https://learn.microsoft.com/ja-jp/dotnet/csharp/fundamentals/functional/pattern-matching

パターンマッチング後のモダンC#の特徴

  • 変数スコープが変更
  • キャスト((T)as)を使うことがまれに
  • switch式と網羅性チェック

対応表

パターン パターンの例 対応する記法・文法・API
型パターン(C#7) obj is int -
宣言パターン(C#7) obj is int intObj -
定数パターン(C#7) obj is false -
ReadOnlySpan<char>
文字列リテラル定数パターン(C#11)
span is "abc" ReadOnlySpan<T>
varパターン obj is var newValue var
破棄パターン(C#8.0) _ -
リレーショナルパターン(C#9.0) >= 3 and < 6 -
論理パターン(C#9.0) obj is A and B and/or/not
プロパティ パターン(C#8.0) segment is { Start.Y: 0 } オブジェクト初期化子
位置指定パターン(C#8.0) (0, 0) コンストラクタ、タプル記法、分解
リスト パターン(C#11) list is [] Index/Range(C#8.0) , コレクション式(C#12)
パターンマッチング全般 - switch式(C#8.0)

モダンC#でよく使うもの

void DoSomething(string? x)
{
    //早期リターン
    if(x is not string x2){ return; }
    Console.WriteLine(x2);

    if(x is not {} x3){ return; }
    Console.WriteLine(x3);
}
void DoSomething2(int? x)
{
    //nullチェック&変数宣言
    if(x is {} x2)
    {
        x2 += 10;
    }else
    {
        //ここもx2のスコープ
        x2 = 0;
    }
    //実はここもx2のスコープ
    Console.WriteLine(x2);
}
いぬいぬいぬいぬ

シンプルプログラム

  • 省略・スクリプトライク
    • global using
    • Mainメソッドの省略
    • クラスの省略
    • using alias C#12
    • record, record struct
    • プライマリコンストラクタ
  • インデントが1段階減らせる
    • file scope namespace
    • using宣言
  • 共通構文
    • Index/Range ([a..b])
    • コレクション式
  • 生文字列リテラル
  • ソースジェネレータ

推奨 非推奨例外
namespace namespace A; namespace A{
}
using using var a = new A(); using(var a = new A()){
}
いぬいぬいぬいぬ

null安全

  • C#8.0で「null安全」に
  • #nullable enable or <Nullable>enable</Nullable>
  • ??=演算子も8.0から
  • そろそろ「モダン」は卒業して「基本」になりそう
いぬいぬいぬいぬ

[.NET API] Native AOT Ready

  • モダンC#2024ではNativeAOTに対応しているかどうか、も重要な視点に
    • 特にライブラリの場合
    • 普通のアプリでも速度や起動時間が全然変わるので影響大きい
  • Native AOT Readyに書く
    • リフレクション避ける
    • 対応ライブラリを使う
    • ソースジェネレータを使う

https://zenn.dev/inuinu/articles/csharp-native-aot

Native AOT ready libraries

  • 定番ライブラリもまだまだ非対応があるのでちゃんと調べる
  • Json
    • Json.NETではなく、System.Text.Json (SG版)
  • 正規表現
    • RegexではなくGeneratedRegex
  • MVVM
    • CommunityToolkit.MVVM ver8.3~
いぬいぬいぬいぬ

細かい

スプレッド要素

List<int> listA = [1,2,3];
List<int> listB = [ .. listA, 4, 5];
  • JavaScriptの「スプレッド構文」、Pythonの「スター演算子」に似たもの
  • コレクション式の[]の中で.. collectionと書くとコレクションの中身が展開される
  • コレクション式と同時に導入
    • コレクション式の中でのみ使える記法
  • Rangeと記法は似ているが厳密には別物
//listBの後ろはRange、前はspread要素
List<int> listC = [ .. listB[1..], .. listB[..1] ];
いぬいぬいぬいぬ

"Safe pointer"

  • ref struct(C#8.0)
  • ref field / readonly ref field (C#11.0)
  • Span<T>/ReadOnlySpan<T>のref struct化 (C#11.0)
  • scoped (C#11.0)
  • ref readonly 引数 (C#12.0)
  • allows ref struct (C#13.0予定)

※ChatGPT 4oによる解説

型引数におけるrefinoutの使用はC#の型システムの制約によって制限されています。以下は、型引数として使用可能かどうかを含めた更新版の表です。

キーワード 引数(メソッドパラメータ) 戻り値(メソッドの返り値) ローカル変数宣言 メソッド内の変数代入 型引数
ref ✔️ ✔️ ✔️ ✔️
readonly ref ✔️ ✔️ ✔️
scoped ref ✔️ ✔️
ref readonly ✔️ ✔️
scoped ref readonly ✔️ ✔️
ref struct ✔️ (3) ✔️ ✔️
readonly ref readonly ✔️ ✔️
scoped ref struct ✔️ (3) ✔️
readonly ref struct ✔️ ✔️
scoped readonly ref struct ✔️ ✔️
in ✔️
out ✔️ ✔️ ✔️

説明:

  • ref: 型引数としては使用できません。これは、参照型や値型の柔軟性が必要なため、型引数での使用は許可されていません。
  • readonly ref: 型引数としても同様に使用できません。readonlyであっても型引数としての参照渡しには制限があります。
  • scoped ref: 型引数には使えません。スコープの制約があるため、型引数としての使用には向いていません。
  • ref readonly: 型引数には使えません。読み取り専用の参照渡しも型引数としては制約されます。
  • scoped ref readonly: 同様に、型引数としては使用できません。
  • ref struct: 構造体の型引数としては使用できませんが、ref struct自体はローカル変数や引数として使うことができます。
  • readonly ref readonly: 型引数には使用できません。
  • scoped ref struct: 型引数には使用できません。
  • readonly ref struct: 型引数には使用できません。
  • scoped readonly ref struct: 型引数には使用できません。
  • in: 読み取り専用の参照渡しで、引数に使えますが、型引数には使えません。
  • out: 型引数に対してはoutキーワードが使えます。outは、型の共変性を指定するためにジェネリック型パラメータで使用されます。

注釈:

  • (3) ref structscoped ref structは、スタック割り当てに関する制約が強いため、通常の型引数としては使用できません。