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
のキーワードを使う際、どれが引数をメソッド内で変更でき、変更がメソッドを呼び出した側に反映されることを意味しますか?
in
out
ref
- これらのどれでもない
クイズ2:
C# で yield
キーワードを使用する主な目的は何ですか?
クイズ3:
C# で dynamic
キーワードを使用すると、何が起こりますか?
クイズ4:
C# で LINQ を使用する際、Select
と SelectMany
の主な違いは何ですか?
Select
は単一の値を返し、SelectMany
は複数の値を返すSelect
はコレクションを返し、SelectMany
は単一の値を返すSelect
はシーケンス内の各要素に対して単一の値を生成し、SelectMany
はシーケンス内の各要素に対してシーケンスを生成する- これらは基本的に同じである
答え:
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を読むことはできる }
要約すると、引数をメソッド内で変更してその変更がメソッドを呼び出した側に反映されるのはref
とout
の場合です。ただし、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
ステートメントごとに一つずつ値が返されます。
利点:
- 遅延評価: シーケンスの要素は要求されるまで生成されません。
- メモリ効率: 大きなコレクションを一度にメモリにロードする代わりに、必要に応じて要素を生成できます。
- コードの簡潔さ: イテレータを手動で実装する代わりに、
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)の機能を活用することができます。
主な特徴と影響:
- 動的型付け:
dynamic
型の変数は、コンパイル時には型が決まっていません。型の解決は実行時に行われます。 - 実行時の型チェック:
dynamic
型の変数に対する操作は、実行時に型チェックされます。これにより、コンパイル時にはエラーが検出されないが、実行時にエラーが発生する可能性があります。 - 動的メソッド呼び出し:
dynamic
型のオブジェクトに対して、存在しないメソッドやプロパティを呼び出すことがコンパイル時には許されますが、実行時にはエラーとなります。 - 動的言語の統合:
dynamic
キーワードを使用することで、C#プログラム内でPythonやRubyなどの動的言語のコードを簡単に統合することができます。 - パフォーマンスの低下:
dynamic
型の変数を使用すると、実行時のオーバーヘッドが増加し、パフォーマンスが低下する可能性があります。
例:
dynamic x = 10; dynamic y = "hello"; Console.WriteLine(x * x); // 100 Console.WriteLine(y.ToUpper()); // "HELLO" // Console.WriteLine(y.NonExistentMethod()); // 実行時エラー: 'string' に 'NonExistentMethod' という名前のメソッドは存在しません。
上記のコードでは、dynamic
型の変数x
とy
が宣言されています。これらの変数は実行時に型が解決され、適切な操作が行われます。しかし、存在しないメソッドを呼び出した場合は、実行時にエラーが発生します。
dynamic
キーワードは柔軟性を提供しますが、その使用は慎重に行う必要があります。静的型検査の利点を失うため、バグを見逃しやすくなり、パフォーマンスにも影響を与える可能性があります。
参考資料・クイズ3
dynamic - C# によるプログラミング入門 | ++C++; // 未確認飛行 C https://ufcpp.net/study/csharp/sp4_dynamic.html
クイズ4:
C#でLINQ(Language Integrated Query)を使用する際、Select
とSelectMany
はコレクションの要素を変換するために使用されますが、それらの動作は異なります。
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
私の回答
- 3 (
ref
) - 2 (非同期処理を中断して後で再開する) 誤答
- 1 (型チェックが実行時に行われる)
- 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<T>について 2023/9/26
ChatGPT製 C#クイズ sealed、Task/ValueTask、StringBuilder、ReadOnlySequence<T>について 2023/9/26
C#の勉強のために、ChatGPTにC#に関するクイズを出してもらいました。
入力
C# 4択の上級クイズ いくつか出して 答えはまとめて末尾に書いて
返答
もちろん、C#に関する上級レベルの4択クイズをいくつか作成しました。
クイズ1:
sealed
キーワードが付けられたクラスはどうなりますか?
クイズ2:
Task
とValueTask
の主な違いは何ですか?
Task
は非同期、ValueTask
は同期ValueTask
はスタックに割り当てられ、Task
はヒープに割り当てられるValueTask
は例外をキャッチできないTask
はasync
キーワードが必要、ValueTask
は不要
クイズ3:
C#のstring
とStringBuilder
の間にある主なパフォーマンス上の違いは何ですか?
string
はガベージコレクションによって管理されるStringBuilder
は複数の文字列操作で効率的string
はUTF-8、StringBuilder
はUTF-16を使用StringBuilder
は読み取り専用、string
は読み書き可能
クイズ4:
C# 8.0で導入されたReadOnlySequence<T>
はどのようなシナリオで便利ですか?
- 多次元配列の操作
- 非同期プログラミングでのスレッドセーフな処理
- メモリ効率的なシーケンスのスライス操作
- 高精度数値計算
答え: 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# の Task
と ValueTask
は非同期操作を表現するための型ですが、いくつか重要な違いがあります。
Task
ヒープ割り当て:
Task
インスタンスはヒープに割り当てられます。これは頻繁な割り当てと解放によるガベージコレクションの負荷がある場合があります。再利用不可:
Task
オブジェクトは完了後に再利用することはできません。一般的な非同期操作: 一般的に、
Task
は長時間の非同期操作に適しています。Task.Delay, Task.Run, Task.WhenAll, Task.WhenAny など:
Task
には非同期操作を行うための多くの静的メソッドが用意されています。
ValueTask
スタック割り当て:
ValueTask
は値型(struct
)なので、スタック上に割り当てられる可能性があります。これにより、ガベージコレクションの負荷が減少する可能性があります。再利用可能:
ValueTask
はIValueTaskSource
を使用して再利用することができる場合があります。短時間の非同期操作: 短時間で完了する非同期操作や、同期的に完了する可能性が高い操作に適しています。
API が限られる:
ValueTask
にはTask
と比べて利用できるAPIが少ないです(例えば、WhenAll
やWhenAny
などの静的メソッドはありません)。
どちらを使用するか?
非同期メソッドが大半の場合で非同期に動作し、結果を待つ時間が長い場合は、
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:
string
とStringBuilder
の主な違いは、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
の使用が推奨されます。
このように、用途に応じてstring
とStringBuilder
のどちらを使用するかを選ぶことが重要です。
参考資料・クイズ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>
は、テキスト処理やバイナリ解析でも非常に有用です。例えば、大きなJSONやXMLドキュメントを処理する場合、全体を一度にメモリにロードする必要はありません。
ゼロコピー操作
一部の高度なシナリオでは、ゼロコピー(データをコピーせずに操作する)が必要です。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
私の回答
- 2
- 4 ×誤答
- 2
- 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
コードの目的は何ですか?
- 非同期処理を高速化する
- ポインタ操作やメモリ操作を可能にする
- セキュリティを強化する
- データベース接続を安定化する
クイズ2:
async
/await
を使用すると何が可能になりますか?
- マルチコアの効率的な使用
- 非同期プログラミング
- インメモリデータベースの操作
- ジェネリクスの効率的な利用
クイズ3:
C#でジェネリクスの制約を指定するために用いられるキーワードは何ですか?
where
constraint
limit
restrict
クイズ4:
C# 9.0で導入されたwith
キーワードは何のために使用されますか?
- 既存のオブジェクトを複製して変更する
- ファイル操作を簡単に行う
- データのシリアライゼーション
- 例外処理の簡略化
答え:
1. 2
2. 2
3. 1
4. 1
ChatGPTによる解説
クイズ1:
Unsafe
コードの目的は何ですか?
2. ポインタ操作やメモリ操作を可能にする
C# の unsafe
コードは、ポインタ操作や直接的なメモリ操作が必要な場合に使用されます。通常、C# はメモリ安全な言語ですが、unsafe
キーワードを用いることで、CやC++のような低レベルのメモリ操作が可能になります。
目的:
パフォーマンス: 直接的なメモリ操作をすることで、特定のケースでパフォーマンスが向上する可能性があります。
相互運用性: CやC++などの他の言語で書かれたライブラリとの連携が必要な場合、
unsafe
コードが役立つことがあります。
例:
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#のジェネリクスで型パラメータに制約を適用するために使用されます。これにより、ジェネリクスを使用する際により柔軟かつ安全なコードが書けます。
目的:
型の制限: 特定の基底クラスを継承している、または特定のインターフェイスを実装しているなど、型に制約をかけることができます。
メンバの利用: 制約によって確定した型が持つメソッドやプロパティにアクセスできます。
具体的な使い方:
- 基底クラス制約
public class MyGenericClass<T> where T : MyBaseClass { // ... }
- インターフェイス制約
public class MyGenericClass<T> where T : IEnumerable { // ... }
- new() 制約
public class MyGenericClass<T> where T : new() { public T CreateInstance() { return new T(); } }
- 複数の制約
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 };
この例では、newPerson
はperson
のコピーを作成し、その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
私の回答
- 2 ○
- 4 ✕誤答
- 1 ○
- 1 ○
でした。全問正解したかった…
感想
「ジェネリクス」が何のことか自分にはピンときていませんでした。「ジェネリック」と同義で、クラス名の後につくTとかTResultとかのことですね。 where T : Enum という形で何回か使ったことがあります。忘れてました。
with式のありがたみを未だによく分かっていないのですが、多分普段からイミュータブルなオブジェクトをその機能を活かせず使ってるからでしょうね。あんま良くないと思います。 string型ですらイミュータブルなのに。あとUnityのVector3構造体とかもそう。 イミュータブルなコレクションや構造体を扱うときに活用したいと思います。
注意
これらの問題および解説はChatGPTのGPT-4 (ChatGPT August 3 Version)にしてもらいました。私も参考資料を確認して間違いがなさそうというのを一応確認しています。が、間違ってるところがあったらどんどん指摘してください。