📦

【C#】たまに「あれこれ何だっけ?」ってなる記法

に公開1

チートシート(Cheat Sheet)

C#記法 処理内容 備考
=> 式形式のメンバー / ラムダ式 メソッドやプロパティを1行で書く
?. Null条件演算子 変数が null でなければ右を実行
?? Null合体演算子 左辺が null なら右辺の値を返す
$ 文字列補間 文字列内に変数を埋め込む
var 型推論 右辺から型を自動決定。Rubyのような動的型付けに近い感覚で書ける。
is 型パターンマッチング 型を判定し、正しければ変数に代入
as 安全なキャスト 特定の型への変換を試み、失敗したら null を返す
out 出力パラメータ 戻り値以外で値を返す(初期化されていない変数も可)
ref 参照渡し 初期化済みの変数そのものを共有する(メソッド内での代入が呼び出し元に反映される)
yield return 反復子 列挙子を1つずつ生成して返す。Rubyの Enumerator.newyield(列挙側)に近い。

1. =>(ラムダ宣言・式形式のメンバー)

C#では 「Fat Arrow(ファットアロー)」 と呼ばれ、メソッドやプロパティを簡潔に書くために使う

  • 式形式のメンバー(Expression-bodied members)
    Rubyの「暗黙の戻り値」に近い感覚で、1行のメソッドやプロパティを定義できる
public string Name => "Taro"; 
public int Add(int a, int b) => a + b;

2. Null(nil)許容・合体演算子

  • ?.(Null条件演算子)
    Rubyのぼっち演算子(&.)と同じ
var length = user?.Name?.Length;
  • ??(Null合体演算子)
    Rubyの || によるデフォルト値設定に近い
string display = name ?? "Unknown";
  • ??=(Null合体代入)
    Rubyの ||= と同じ、nullなら代入する
_cache ??= LoadData();

3. 文字列操作:$@

  • $"{var}"(文字列補間)
    先頭に $ をつけると、{ } 内に変数や式を書ける
string msg = $"Hello, {name}!";
  • @"..."(逐語的文字列リテラル)
    エスケープシーケンス(\nなど)を無視し、改行をそのまま書ける(パスの記述に便利)
string path = @"C:\Users\Documents";
string multiLine = @"1行目
2行目";

4. パラメータ修飾子:outref

Ruby にはない概念で、「メソッド内で書き換えた値を呼び出し元に持ち帰る」 ための記法

  • out(出力パラメータ)
    複数の戻り値を返したい時によく使われる
if (int.TryParse("123", out int result)) {
    // result に 123 が入る
}
  • ref
    「変数そのものを共有する」という強力な記法
    値を入れ替える、呼び出し元の変数の中身が完全に置き換わる
public void Test()
{
    int x = 10;
    int y = 20;

    // ref をつけて渡す(呼び出し側も明示が必要)
    Swap(ref x, ref y);

    Console.WriteLine($"x: {x}, y: {y}"); // 結果:x: 20, y: 10
}

public void Swap(ref int a, ref int b)
{
    int temp = a;
    a = b;
    b = temp; // 呼び出し元の x と y が直接書き換わる
}

6. 型判定とキャスト:isas

Rubyの is_a?instance_of? に相当、判定と同時に変数への代入が可能

  • is(型パターンマッチング)
if (obj is string s) {
    Console.WriteLine(s.Length); // objがstringならsとして使える
}
  • as(安全なキャスト)
    型が違ってもエラーにならず、null を返す
    直接キャスト (string)obj を使うと型が違う場合にプログラムが停止するが、as なら null が返るだけなので安全
object myData = 123; // 中身は int
// string str1 = (string)myData; // ここで例外 InvalidCastException が発生してアプリが落ちる
string str2 = myData as string; 
if (str2 == null) {
    Console.WriteLine("変換に失敗したので、null になりました");
}

7. 列挙:yield return

Rubyの yield は「ブロックの実行」だが、C#の yield return「列挙子(Enumerator)を1つずつ生成する」 ために使う

// 呼び出されるたびに次の値を返す「イテレータ」を生成
public IEnumerable<int> GetNumbers() {
    yield return 1;
    yield return 2;
    yield return 3;
}

Discussion

いぬいぬいぬいぬ
  1. Null(nil)許容・合体演算子

つい最近(C#14)ですが、「null条件代入」がこれの仲間に加わりました。

//null条件代入
a?.b?.c = 123;

//従来の書き方
if(a is not null){
    if(a.b is not null){
        a.b.c = 123;
    }
}

https://ufcpp.net/study/csharp/rm_nullusage.html#null-conditional-assignment

@"..."(逐語的文字列リテラル)
エスケープシーケンス(\nなど)を無視

「逐語的文字列リテラル」は"{}にエスケープが必要になるため、一つ上で紹介している「文字列補間」などと相性が悪いです。
そこで今のモダンC#では「生文字列リテラル」と言う記法を使います。

生文字列リテラル(raw string literal)
var doc = "Documents";
var path = $"""C:\Users\{doc}""";

var multiLine = """
    1行目
    2行目
    """;
    //↑生文字列リテラルはここでdedent処理してくれます

https://ufcpp.net/study/csharp/st_string.html?p=2#raw-string

パラメータ修飾子:out と ref

パラメータ修飾子にはもう一つinもあります(読み取り専用の参照渡し)。

https://ufcpp.net/study/csharp/sp_ref.html#in

ref/in/out引数は3つで1セット、みたいなところがあるので併せて覚えておくといいと思います。

is(型パターンマッチング)

「型パターン(型宣言パターン)」はisだけじゃなくてswitch式/文でも使えます。この場合、isは使っていなくとも同じであることに注意ですね。

static string F(object obj) => obj switch
{
        string s => $"{s} : utf16 strings",
        int i => $"{i} : 32bit signed integer",
        _ => $"{obj} : unknown",
};

https://ufcpp.net/study/csharp/datatype/typeswitch/?p=5#switch-expression