C#好きの勉強ノート

C#やUnityを仕事で使ってきたエンジニアの勉強ノートです。

ChatGPT製 C#クイズ 参照渡し、yield、dynamic、SelectMany について 2023/11/02

ChatGPT製 C#クイズ 参照渡し、yield、dynamic、SelectMany について 2023/11/02

C#の勉強用に、ChatGPTにC#に関するクイズを出してもらいました。

入力

C# 4択の上級クイズ 日本語でいくつか出して 答えは選択肢といっしょにまとめて末尾に書いて

返答

もちろんです。C#に関する上級レベルのクイズを日本語でいくつか作成しました。

クイズ1:

C#in, out, ref のキーワードを使う際、どれが引数をメソッド内で変更でき、変更がメソッドを呼び出した側に反映されることを意味しますか?

  1. in
  2. out
  3. ref
  4. これらのどれでもない

クイズ2:

C#yield キーワードを使用する主な目的は何ですか?

  1. イテレータを作成する
  2. 非同期処理を中断して後で再開する
  3. メモリアロケーションを最適化する
  4. 例外処理を簡略化する

クイズ3:

C#dynamic キーワードを使用すると、何が起こりますか?

  1. 型チェックがコンパイル時ではなく、実行時に行われる
  2. オブジェクトが動的に生成される
  3. メソッドが動的にオーバーロードされる
  4. クラスが動的に生成される

クイズ4:

C#LINQ を使用する際、SelectSelectMany の主な違いは何ですか?

  1. Select は単一の値を返し、SelectMany は複数の値を返す
  2. Select はコレクションを返し、SelectMany は単一の値を返す
  3. Select はシーケンス内の各要素に対して単一の値を生成し、SelectMany はシーケンス内の各要素に対してシーケンスを生成する
  4. これらは基本的に同じである

答え: 1. 3 (ref) 2. 1 (イテレータを作成する) 3. 1 (型チェックが実行時に行われる) 4. 3 (Select はシーケンス内の各要素に対して単一の値を生成し、SelectMany はシーケンス内の各要素に対してシーケンスを生成する)

ChatGPTによる解説

クイズ1:

C#では、メソッドの引数を渡す方法として、in, out, refの三つのキーワードが提供されています。これらはそれぞれ異なる挙動を持っています。

ref

  • refキーワードを使用すると、引数を「参照渡し」にできます。
  • メソッド内で引数の値を変更すると、その変更はメソッドを呼び出した側にも反映されます。
  • メソッドを呼び出す際に、引数にrefキーワードを付ける必要があります。
  • 引数として渡す変数は初期化されている必要があります。
public void RefExample(ref int x)
{
    x = 10; // この変更は呼び出し元にも反映される
}

out

  • outキーワードも「参照渡し」を行います。
  • しかし、outはメソッド内で引数の値を必ず設定しなければならないというルールがあります。
  • メソッドを呼び出す際に引数にoutキーワードを付ける必要があります。
  • 引数として渡す変数は初期化されていなくてもかまいません。
public void OutExample(out int x)
{
    x = 20; // この値は呼び出し元に反映される
}

in

  • inキーワードは引数を「読み取り専用の参照渡し」として渡します。
  • メソッド内で引数の値を変更することはできませんが、変更を禁止するのは引数が指すオブジェクトの変更を防ぐためであり、引数自体の参照を変更することは禁止されていません。
  • メソッドを呼び出す際に引数にinキーワードを付けることができますが、必須ではありません。
  • inは主にパフォーマンスの最適化のために使用されます(特に大きな構造体を渡す際にコピーのコストを避けるため)。
public void InExample(in int x)
{
    // x = 30; // コンパイルエラー: 'in' パラメーター 'x' は読み取り専用であり、代入できません
    Console.WriteLine(x); // xを読むことはできる
}

要約すると、引数をメソッド内で変更してその変更がメソッドを呼び出した側に反映されるのはrefoutの場合です。ただし、outは引数をメソッド内で設定しなければならないという特別なルールがあります。inは引数を読み取り専用にします。

参考資料・クイズ1

in/out/refパラメーター修飾子の違いとは?[C#]:.NET TIPS - @IT https://atmarkit.itmedia.co.jp/ait/articles/1804/25/news021.html

クイズ2:

C#においてyieldキーワードは、イテレータメソッドを簡単に作成するために使用されます。イテレータメソッドは、コレクションの要素を一つずつ遅延評価で返すことができるメソッドです。yield returnを使用すると、メソッドの実行を一時停止し、現在の値を返すことができます。その後、イテレーションが続けられると、メソッドの実行が停止した場所から再開されます。

yieldキーワードを使用することで、大量のデータを一度にメモリにロードすることなく、要求に応じてデータを生成し返すことができます。これはメモリ使用量を節約し、パフォーマンスを向上させることができます。

例:

public static IEnumerable<int> GetNumbers()
{
    yield return 1;
    yield return 2;
    yield return 3;
}

上記のコードでは、GetNumbersメソッドは3つの数を含むシーケンスを生成します。このメソッドを呼び出すと、実際にはすぐには実行されません。代わりに、IEnumerable<int>を実装する特殊な型のオブジェクトが返されます。このオブジェクトを通じてイテレートすると、GetNumbersメソッドの実行が開始され、yield returnステートメントごとに一つずつ値が返されます。

利点:

  1. 遅延評価: シーケンスの要素は要求されるまで生成されません。
  2. メモリ効率: 大きなコレクションを一度にメモリにロードする代わりに、必要に応じて要素を生成できます。
  3. コードの簡潔さ: イテレータを手動で実装する代わりに、yieldキーワードを使用することで、コードが簡潔になります。

yieldキーワードは、特にデータのストリームを扱う際や、大量のデータを効率的に処理する必要がある場合に非常に便利です。

参考資料・クイズ2

yield ステートメント - 反復子で次の要素を指定する - C# | Microsoft Learn https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/statements/yield
イテレーター - C# によるプログラミング入門 | ++C++; // 未確認飛行 C https://ufcpp.net/study/csharp/sp2_iterator.html

クイズ3:

C#dynamicキーワードを使用すると、実行時に型の検査をバイパスし、コンパイル時の静的型検査を無効にすることができます。これにより、コンパイル時ではなく実行時に型の解決を行う「動的言語ランタイム」(DLR)の機能を活用することができます。

主な特徴と影響:

  1. 動的型付け: dynamic型の変数は、コンパイル時には型が決まっていません。型の解決は実行時に行われます。
  2. 実行時の型チェック: dynamic型の変数に対する操作は、実行時に型チェックされます。これにより、コンパイル時にはエラーが検出されないが、実行時にエラーが発生する可能性があります。
  3. 動的メソッド呼び出し: dynamic型のオブジェクトに対して、存在しないメソッドやプロパティを呼び出すことがコンパイル時には許されますが、実行時にはエラーとなります。
  4. 動的言語の統合: dynamicキーワードを使用することで、C#プログラム内でPythonRubyなどの動的言語のコードを簡単に統合することができます。
  5. パフォーマンスの低下: dynamic型の変数を使用すると、実行時のオーバーヘッドが増加し、パフォーマンスが低下する可能性があります。

例:

dynamic x = 10;
dynamic y = "hello";
Console.WriteLine(x * x); // 100
Console.WriteLine(y.ToUpper()); // "HELLO"
// Console.WriteLine(y.NonExistentMethod()); // 実行時エラー: 'string' に 'NonExistentMethod' という名前のメソッドは存在しません。

上記のコードでは、dynamic型の変数xyが宣言されています。これらの変数は実行時に型が解決され、適切な操作が行われます。しかし、存在しないメソッドを呼び出した場合は、実行時にエラーが発生します。

dynamicキーワードは柔軟性を提供しますが、その使用は慎重に行う必要があります。静的型検査の利点を失うため、バグを見逃しやすくなり、パフォーマンスにも影響を与える可能性があります。

参考資料・クイズ3

dynamic - C# によるプログラミング入門 | ++C++; // 未確認飛行 C https://ufcpp.net/study/csharp/sp4_dynamic.html

クイズ4:

C#LINQ(Language Integrated Query)を使用する際、SelectSelectManyはコレクションの要素を変換するために使用されますが、それらの動作は異なります。

Select

Selectメソッドは、コレクションの各要素を新しい形に変換します。結果は、変換された要素を含む新しいコレクションです。

var numbers = new[] { 1, 2, 3, 4 };
var squares = numbers.Select(n => n * n); // 結果: { 1, 4, 9, 16 }

上記の例では、Selectを使用して各数字をその平方に変換しています。

SelectMany

SelectManyメソッドは、コレクションの各要素をコレクションに変換し、その結果として得られるコレクションのシーケンスを単一の平坦化されたシーケンスに変換します。

var lists = new List<List<int>>
{
    new List<int> { 1, 2 },
    new List<int> { 3, 4 }
};
var flattened = lists.SelectMany(list => list); // 結果: { 1, 2, 3, 4 }

上記の例では、SelectManyを使用してリストのリストを単一のリストに平坦化しています。

主な違い

  • Selectは単一の要素を別の要素に変換します。
  • SelectManyは単一の要素をコレクションに変換し、最終的な結果は平坦化された単一のシーケンスです。

SelectManyはしばしば多対多のリレーションシップを扱う際に便利ですが、Selectは一般的に単一の要素に対する変換を行う場合に使用されます。

参考資料・クイズ4

標準クエリ演算子(クエリ式関係) - C# によるプログラミング入門 | ++C++; // 未確認飛行 C https://ufcpp.net/study/csharp/sp3_stdquery.html

私の回答

  1. 3 (ref)
  2. 2 (非同期処理を中断して後で再開する) 誤答
  3. 1 (型チェックが実行時に行われる)
  4. 1 (Select は単一の値を返し、SelectMany は複数の値を返す) 誤答 

でした。

感想

クイズ1

inキーワードってそんなに使ったことがないんですよね。大きい構造体を使ったことがないというか。そもそもサイズが大きいものは構造体ではなくクラスを使うべきですよね… 今後やむを得ず大きい構造体を使うときが来たらやってみようと思います。

クイズ2

yield returnが非同期処理に使われるのはUnityの「コルーチン」を使うときですね。このコルーチンはasync/awaitがまだC#になかった時代に対応するためにIEnumeratorを使って非同期処理を実現したものです。この場合のyield returnは「次のフレームに非同期処理を持ち越す」という動作になります。 これはyield returnの機能の一つではあるのですが、主目的ではないですよね。反省です。

クイズ3

dynamicは実務で使ったことが何度かありますね。DLLの読み込みをしてDLL内の特定のメソッドを呼び出すときに使います。 VB.NETのObjectも似たような動作になります。これをカジュアルに使えるVB.NET、なかなかイカれた言語ですよね… C# では型のチェックを自動でしたいだけならvarキーワードを使うだけでコンパイラ側で勝手に型を指定してくれます。.NET内で解決する問題なら基本的にはvarで全く問題ないので、できるだけdynamicは使いたくないですね。

クイズ4

SelectManyは平坦化、使ってないと忘れますね。あんまりデータを平坦化したいと思ったことがないのですが、今後使うこともあるでしょう。 Selectで複数の値を返す処理を何度も書いたはずなのに、なぜ間違えてしまうのか、自分…

注意

これらの問題および解説はChatGPTのGPT-4 (ChatGPT August 3 Version)にしてもらいました。C# 10.0以降のデータをChatGPTが保持していない、などの理由で現在の最新バージョンとは正誤が異なる可能性があります。私も参考資料を確認して間違いがなさそうというのを一応確認していますが、間違ってるところがあったらどんどん指摘してください。

ChatGPT製 C#クイズ sealed、Task/ValueTask、StringBuilder、ReadOnlySequence&lt;T&gt;について 2023/9/26

ChatGPT製 C#クイズ sealed、Task/ValueTask、StringBuilder、ReadOnlySequence<T>について 2023/9/26

C#の勉強のために、ChatGPTにC#に関するクイズを出してもらいました。

入力

C# 4択の上級クイズ いくつか出して 答えはまとめて末尾に書いて

返答

もちろん、C#に関する上級レベルの4択クイズをいくつか作成しました。

クイズ1:

sealed キーワードが付けられたクラスはどうなりますか?

  1. インスタンス化できなくなる
  2. 継承できなくなる
  3. シリアライズできなくなる
  4. イミュータブルになる

クイズ2:

TaskValueTaskの主な違いは何ですか?

  1. Taskは非同期、ValueTaskは同期
  2. ValueTaskはスタックに割り当てられ、Taskはヒープに割り当てられる
  3. ValueTaskは例外をキャッチできない
  4. Taskasyncキーワードが必要、ValueTaskは不要

クイズ3:

C#stringStringBuilderの間にある主なパフォーマンス上の違いは何ですか?

  1. stringガベージコレクションによって管理される
  2. StringBuilderは複数の文字列操作で効率的
  3. stringUTF-8StringBuilderUTF-16を使用
  4. StringBuilderは読み取り専用、stringは読み書き可能

クイズ4:

C# 8.0で導入されたReadOnlySequence<T>はどのようなシナリオで便利ですか?

  1. 多次元配列の操作
  2. 非同期プログラミングでのスレッドセーフな処理
  3. メモリ効率的なシーケンスのスライス操作
  4. 高精度数値計算

答え: 1. 2 2. 2 3. 2 4. 3

ChatGPTによる解説

クイズ1:

C#においてsealedキーワードがクラスに付けられると、そのクラスは継承できなくなります。言い換えれば、他のクラスがsealedが付与されたクラスを基底クラスとして使用することはできません。これは、特定のクラスの実装がそのままの状態で保たれるべきであり、サブクラスによって変更されるべきではない場合などに有用です。

例:

以下はsealedが付与されたクラスの例です。

public sealed class MySealedClass
{
    public int MyProperty { get; set; }
}

このクラスは継承できません。

public class MyDerivedClass : MySealedClass // コンパイルエラー
{
    // ...
}

このように、MySealedClassを継承しようとするとコンパイルエラーが発生します。

主な用途:

  • 不変性の確保: クラスの継承が許可されないことで、その挙動が変更されることがなく、より安全なコードが書けます。

  • 最適化: コンパイラsealedクラスが継承されないことが保証されるため、より効率的なコードを生成する可能性があります。

  • 明示性: sealedキーワードは、そのクラスが継承によって拡張されるべきでないという設計意図を明示的に示します。

sealedキーワードは、クラスだけでなく、メソッドやプロパティにも適用されることがあります。これにより、継承したクラスが基底クラスの特定のメソッドやプロパティをオーバーライドできなくなります。

参考資料・クイズ1

https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/sealed

https://qiita.com/sator_imaging/items/0413c30716c6e5df5cd3

https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/#peanut-butter

クイズ2:

C#TaskValueTask は非同期操作を表現するための型ですが、いくつか重要な違いがあります。

Task

  • ヒープ割り当て: Task インスタンスはヒープに割り当てられます。これは頻繁な割り当てと解放によるガベージコレクションの負荷がある場合があります。

  • 再利用不可: Task オブジェクトは完了後に再利用することはできません。

  • 一般的な非同期操作: 一般的に、Task は長時間の非同期操作に適しています。

  • Task.Delay, Task.Run, Task.WhenAll, Task.WhenAny など: Task には非同期操作を行うための多くの静的メソッドが用意されています。

ValueTask

  • スタック割り当て: ValueTask は値型(struct)なので、スタック上に割り当てられる可能性があります。これにより、ガベージコレクションの負荷が減少する可能性があります。

  • 再利用可能: ValueTaskIValueTaskSource を使用して再利用することができる場合があります。

  • 短時間の非同期操作: 短時間で完了する非同期操作や、同期的に完了する可能性が高い操作に適しています。

  • API が限られる: ValueTask には Task と比べて利用できるAPIが少ないです(例えば、WhenAllWhenAny などの静的メソッドはありません)。

どちらを使用するか?

  • 非同期メソッドが大半の場合で非同期に動作し、結果を待つ時間が長い場合は、Task の使用が適しています。

  • 非同期メソッドが大半の場合で同期的に完了する(つまり、待つ時間が非常に短い)または非常に頻繁に呼び出される場合、ValueTask の使用が適している可能性があります。

それぞれの使用ケースに応じて、適切な方を選択することが重要です。

参考資料・クイズ2

https://learn.microsoft.com/ja-jp/dotnet/api/system.threading.tasks.task?view=net-7.0

https://learn.microsoft.com/ja-jp/dotnet/api/system.threading.tasks.valuetask?view=net-7.0

https://ufcpp.net/study/csharp/sp5_async.html#valuetask

https://www.buildinsider.net/column/iwanaga-nobuyuki/009

クイズ3:

stringStringBuilderの主な違いは、stringが不変(immutable)であるのに対し、StringBuilderが可変(mutable)であることです。この違いに起因するいくつかのパフォーマンス上の特性があります。

string

  • 不変性: stringは不変です。したがって、一度作成したstringオブジェクトは変更できません。

  • 連結コスト: 2つのstringを連結する場合、新しいstringインスタンスが作成されます。これにより、多くのstringを連結する場合、時間とメモリの両方において高いコストがかかります。

  string result = "";
  for (int i = 0; i < 1000; i++) {
      result += "some string";  // 非効率
  }
  • メモリ: 不変性により、新しいstringを生成するごとに新たなメモリが割り当てられます。

StringBuilder

  • 可変性: StringBuilderは可変です。内容を変更しても新しいオブジェクトが作成されないため、メモリ効率が高く、連結操作も高速です。

  • 連結の効率: 大量の文字列操作、特に連結が必要な場合、StringBuilderは通常、より効率的です。

  StringBuilder sb = new StringBuilder();
  for (int i = 0; i < 1000; i++) {
      sb.Append("some string");  // 効率的
  }
  string result = sb.ToString();
  • メモリ: StringBuilderは初期容量を指定でき、内部の配列が必要に応じてリサイズされます。これにより、多くの小さなメモリ割り当てとガベージコレクションの動作を抑制できます。

いつどれを使うべきか?

  • 短い文字列を少数、繰り返し連結するような場合には、stringを使ってもパフォーマンス上の問題はほとんど発生しません。

  • 大量の文字列操作が必要な場合、特にループ内で多くの連結が行われる場合は、StringBuilderの使用が推奨されます。

このように、用途に応じてstringStringBuilderのどちらを使用するかを選ぶことが重要です。

参考資料・クイズ3

https://learn.microsoft.com/ja-jp/dotnet/api/system.text.stringbuilder?view=net-7.0

https://ufcpp.net/study/csharp/start/improvedinterpolatedstring/

クイズ4:

ReadOnlySequence<T>C# 8.0と.NET Core 2.1で導入され、メモリ効率的なデータ処理が必要なシナリオで有用です。特に、連続していないメモリセグメントにまたがるデータの表現と操作に力を発揮します。以下は、そのようなシナリオのいくつかです。

ネットワークI/Oとストリーム処理

ReadOnlySequence<T>は、ネットワークプログラミングとデータストリームの操作に特に便利です。受信バッファがメモリ上で断片的な場合や、連続していない場合でも、この型を使用すると効率的にデータを処理できます。

パイプライン処理

データパイプラインの各ステージでデータを変更する代わりに、ReadOnlySequence<T>を使用して元のデータに対するビューを変更することができます。これにより、不要なデータのコピーを避けられます。

テキストとバイナリ解析

ReadOnlySequence<T>は、テキスト処理やバイナリ解析でも非常に有用です。例えば、大きなJSONXMLドキュメントを処理する場合、全体を一度にメモリにロードする必要はありません。

ゼロコピー操作

一部の高度なシナリオでは、ゼロコピー(データをコピーせずに操作する)が必要です。ReadOnlySequence<T>は、そのようなケースで有用です。

メモリ効率

ReadOnlySequence<T>は、メモリ効率の向上にも寄与します。連続したメモリブロックではなく、既存のメモリセグメントに対するスライス(部分範囲)を効率的に操作できます。

このように、ReadOnlySequence<T>は多くのシナリオで非常に強力ですが、その複雑性から初心者には少々扱いづらい可能性もあります。ただし、高度なパフォーマンスが必要な場合には、この型が非常に有用です。

参考資料・クイズ4

https://learn.microsoft.com/ja-jp/dotnet/api/system.buffers.readonlysequence-1?view=net-7.0

https://learn.microsoft.com/ja-jp/dotnet/standard/io/buffers

私の回答

  1. 2
  2. 4 ×誤答
  3. 2
  4. 3

でした。非同期難しい…

感想

sealed修飾子によってパフォーマンスが高速化するのは知りませんでした。実行時間が 0.9837 ns から 0.0018 ns になるらしいので劇的に改善しますね。何万回と呼び出すような継承しないクラスには積極的に書こうと思いました。
Task/ValueTaskの使い分けは相当難しそうです。「基本的には同期処理だが、たまに非同期処理をしたい」という場所に使うのがValueTask<TResult>なんだそうです。これまた速度も劇的に上がるとのこと。ただ、いたずらになんでもValueTask<TResult>にしてしまうと、Task<TResult>を扱うメソッドが使えなかったり、値型であるがゆえのミスが起こったりすると思います。それこそ処理速度やメモリ量といったパフォーマンスに大きく影響する場所なので、しっかり勉強しようと思います。
string関連の仕様も、ChatGPTが網羅していないC# 10.0以降(注:C# 10.0のプレビュー版の知識はあるそうです)でどんどん高速化されていっているそうです。ループ内ならStringBuilder、という使い方そのものは変わりませんが、stringをさらに気楽に使えるようになったのは嬉しいですね。文字列操作も高速化の肝ですので、自分の手元でもいろいろ実験してみたいです。
ReadOnlySequence<T> は構造体で、SequenceReader<T> はref structだそうで。私はメモリバッファ関連のコレクションを操作したことがあまりないので、使い方を実感できていません。無力だ…
とはいえHTTPでJSONデータを送受信するプログラムを書いたことがある(1回しかなくて相当苦労したけど)ので、そういう通信関連のコードも勉強しなきゃなあと思います。

注意

これらの問題および解説はChatGPTのGPT-4 (ChatGPT August 3 Version)にしてもらいました。私も参考資料を確認して間違いがなさそうというのを一応確認しています。が、間違ってるところがあったらどんどん指摘してください。

ChatGPT製 C#クイズ 2023/8/25 Unsafe、async/await、ジェネリクスの成約、with

ChatGPT製 C#クイズ Unsafe、async/await、ジェネリクスの成約、with について 2023/8/25

C#の勉強のために、ChatGPTにC#に関するクイズを出してもらいました。

入力

C# 4択の上級クイズ いくつか出して 答えはまとめて末尾に書いて

返答

もちろんです、以下にC#に関する4択の上級クイズをいくつか出題します。

クイズ1:

Unsafeコードの目的は何ですか?

  1. 非同期処理を高速化する
  2. ポインタ操作やメモリ操作を可能にする
  3. セキュリティを強化する
  4. データベース接続を安定化する

クイズ2:

async/awaitを使用すると何が可能になりますか?

  1. マルチコアの効率的な使用
  2. 非同期プログラミング
  3. インメモリデータベースの操作
  4. ジェネリクスの効率的な利用

クイズ3:

C#ジェネリクスの制約を指定するために用いられるキーワードは何ですか?

  1. where
  2. constraint
  3. limit
  4. restrict

クイズ4:

C# 9.0で導入されたwithキーワードは何のために使用されますか?

  1. 既存のオブジェクトを複製して変更する
  2. ファイル操作を簡単に行う
  3. データのシリアライゼーション
  4. 例外処理の簡略化

答え: 1. 2
2. 2
3. 1
4. 1

ChatGPTによる解説

クイズ1:

Unsafeコードの目的は何ですか? 2. ポインタ操作やメモリ操作を可能にする

C#unsafe コードは、ポインタ操作や直接的なメモリ操作が必要な場合に使用されます。通常、C# はメモリ安全な言語ですが、unsafe キーワードを用いることで、CやC++のような低レベルのメモリ操作が可能になります。

目的:

  1. パフォーマンス: 直接的なメモリ操作をすることで、特定のケースでパフォーマンスが向上する可能性があります。

  2. 相互運用性: CやC++などの他の言語で書かれたライブラリとの連携が必要な場合、unsafe コードが役立つことがあります。

  3. 特定のアルゴリズム: 低レベルのメモリ操作が必要な特定のアルゴリズムやデータ構造を実装する際にも使用されます。

例:

unsafe
{
    int var = 10;
    int* ptr = &var;
    Console.WriteLine("Data at ptr: " + *ptr); 
}

この機能はリスクが高く、メモリ不正アクセスバッファオーバーフローなどの問題を引き起こす可能性があるため、慎重に使用する必要があります。そのため、一般的なアプリケーション開発では推奨されませんが、特定のケースで非常に便利であることもあります。

参考資料・クイズ1

unsafe キーワード - C# リファレンス | Microsoft Learn https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/unsafe

unsafe - C# によるプログラミング入門 | ++C++; // 未確認飛行 C https://ufcpp.net/study/csharp/sp_unsafe.html

クイズ2は基本的すぎるので省略

クイズ3:

where キーワードは、C#ジェネリクスで型パラメータに制約を適用するために使用されます。これにより、ジェネリクスを使用する際により柔軟かつ安全なコードが書けます。

目的:

  • 型の制限: 特定の基底クラスを継承している、または特定のインターフェイスを実装しているなど、型に制約をかけることができます。

  • メンバの利用: 制約によって確定した型が持つメソッドやプロパティにアクセスできます。

  • インスタンス作成: new() 制約を用いると、ジェネリクス内で新しいインスタンスを生成することが可能になります。

具体的な使い方:

  1. 基底クラス制約
public class MyGenericClass<T> where T : MyBaseClass
{
    // ...
}
  1. インターフェイス制約
public class MyGenericClass<T> where T : IEnumerable
{
    // ...
}
  1. new() 制約
public class MyGenericClass<T> where T : new()
{
    public T CreateInstance()
    {
        return new T();
    }
}
  1. 複数の制約
public class MyGenericClass<T> where T : MyBaseClass, IEnumerable, new()
{
    // ...
}

where キーワードを用いることで、ジェネリクスをより効率的かつ安全に使うことができます。制約を使って型の能力を明示することで、コンパイル時にエラーを早期に発見したり、必要なメソッドやプロパティに安全にアクセスしたりできます。

参考資料・クイズ3

where (ジェネリック型制約) - C# リファレンス | Microsoft Learn https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/where-generic-type-constraint

ジェネリック - C# によるプログラミング入門 | ++C++; // 未確認飛行 C https://ufcpp.net/study/csharp/sp2_generics.html

クイズ4:

C# 9.0で導入されたwithキーワードは何のために使用されますか? 1. 既存のオブジェクトを複製して変更する

C# 9.0で導入されたwithキーワードは、主にレコード(record)と呼ばれる不変オブジェクトに対して非破壊的な変更を行うために使用されます。withを使用すると、オブジェクトの新しいコピーを作成し、そのコピーの一部のプロパティを変更できます。これにより、不変性を保ちつつオブジェクトの状態を簡単に更新できます。

使用例:

public record Person(string Name, int Age);

var person = new Person("Alice", 30);
var newPerson = person with { Age = 31 };

この例では、newPersonpersonのコピーを作成し、そのAgeプロパティを31に設定します。personオブジェクト自体は変更されず、新しいオブジェクトが生成されます。

withキーワードを使うことで、オブジェクトの不変性を保ちながら効率的に状態の変更を行うことができます。これは特に関数型プログラミングパラダイムに適しており、コードの可読性や保守性にも寄与します。

参考資料・クイズ4

with 式 - 既存のオブジェクトの変更されたコピーである新しいオブジェクトを作成する | Microsoft Learn https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/with-expression

レコード型 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C https://ufcpp.net/study/csharp/datatype/record/#with

私の回答

  1. 2 ○
  2. 4 ✕誤答
  3. 1 ○
  4. 1 ○
    でした。全問正解したかった…

感想

ジェネリクス」が何のことか自分にはピンときていませんでした。「ジェネリック」と同義で、クラス名の後につくTとかTResultとかのことですね。 where T : Enum という形で何回か使ったことがあります。忘れてました。

with式のありがたみを未だによく分かっていないのですが、多分普段からイミュータブルなオブジェクトをその機能を活かせず使ってるからでしょうね。あんま良くないと思います。 string型ですらイミュータブルなのに。あとUnityのVector3構造体とかもそう。 イミュータブルなコレクションや構造体を扱うときに活用したいと思います。

注意

これらの問題および解説はChatGPTのGPT-4 (ChatGPT August 3 Version)にしてもらいました。私も参考資料を確認して間違いがなさそうというのを一応確認しています。が、間違ってるところがあったらどんどん指摘してください。