C#の基本文法や型の扱い、非同期処理、便利な構文まで、開発の現場でよく使われる知識を10問のクイズにまとめました。普段なんとなく書いているコードも、あらためて問われると迷うことがあるかもしれません。初心者の方は基礎の確認に、経験者の方は知識の整理や腕試しにご活用ください。解説も読み進めながら、ひとつずつ理解を深めていきましょう。まずは気軽に挑戦してみてください。
Q1 : Dictionary<string, int> に存在しないキーを、インデクサー dict["x"] で読み取ろうとするとどうなりますか?
Dictionary のインデクサーで存在しないキーを読み取ろうとすると、KeyNotFoundException がスローされます。null や 0 のような既定値は返されません。キーが存在するか不明な場合は、TryGetValue メソッドを使うのが一般的で、キーがあれば true を返して値を out 引数に格納し、なければ false を返します。ContainsKey で事前に確認する方法もありますが、TryGetValue のほうが検索が1回で済むため効率的です。
Q2 : string は不変(イミュータブル)な型です。ループ内などで大量の文字列連結を効率よく行いたい場合に、適したクラスはどれですか?
string は不変であるため、+ 演算子などで連結するたびに新しい文字列オブジェクトが生成され、大量に繰り返すとメモリ確保やコピーのコストが増大します。StringBuilder は内部のバッファーを直接書き換えられる可変の文字列クラスで、Append メソッドで文字列を追加しても毎回新しいオブジェクトを作らないため、ループ内での大量連結に適しています。最終的に ToString メソッドで string に変換します。StringReader は文字列を読み取るためのクラス、StringComparer は文字列の比較用のクラスです。
Q3 : C#のクラスの継承に関する説明として正しいものはどれですか?
C#ではクラスの多重継承が認められておらず、直接継承できる基底クラスは1つだけです。一方で、インターフェースは複数実装することができます。これは、複数の基底クラスを持つことで生じるメンバーの衝突といった曖昧さの問題を避けるための設計です。なお、何も指定しなければすべてのクラスは暗黙的に System.Object を継承します。複数の機能を組み合わせたい場合は、インターフェースの実装や委譲(コンポジション)を活用するのが一般的です。
Q4 : using ステートメントのブロックを抜けるときに、自動的に呼び出されるメソッドはどれですか?
using ステートメントは IDisposable インターフェースを実装したオブジェクトに対して使い、ブロックを抜ける際に、例外が発生した場合でも必ず Dispose メソッドが呼び出されます。これにより、ファイルやデータベース接続などのリソースを確実に解放できます。Close は自動では呼ばれず、Finalize はガベージコレクターが呼び出すもので、呼び出しのタイミングは不定です。Release というメソッドはこの仕組みには関係ありません。C# 8.0 以降は、ブロックを使わない using 宣言も書けます。
Q5 : C# 9.0 で導入された record 型について、== 演算子による比較の既定の挙動として正しいものはどれですか?
record 型は、値の等価性(value equality)を既定でサポートする型です。コンパイラーが Equals や GetHashCode、== 演算子を自動生成し、型が同じで、すべてのインスタンスフィールドの値が等しければ、別のインスタンス同士でも == は true を返します。通常のクラスでは、== は既定で参照の同一性を比較するため、この点が大きな違いです。record には with 式による非破壊的な変更や、ToString の自動生成といった機能もあり、データを保持するための不変オブジェクトを簡潔に定義できます。
Q6 : const と readonly の違いとして正しいものはどれですか?
const はコンパイル時に値が確定する定数で、宣言時に初期化する必要があり、使用箇所にはコンパイル時に値が埋め込まれます。一方、readonly は実行時に値が決まるフィールドで、宣言時またはコンストラクター内で代入でき、その後は変更できません。そのため、インスタンスごとに異なる不変の値を持たせたい場合や、実行時に計算した値を保持したい場合には readonly が適しています。const は暗黙的に static として扱われる点も、readonly との違いのひとつです。
Q7 : 拡張メソッドを定義するとき、拡張対象の型を指定するために第1引数の前に付ける修飾子はどれですか?
拡張メソッドは、既存の型のソースコードを変更せずに新しいメソッドを追加したように呼び出せる機能です。定義するには、静的クラス内の静的メソッドとして宣言し、第1引数の型の前に this 修飾子を付けます。この第1引数が拡張の対象となる型を表します。呼び出す側は、インスタンスメソッドのように obj.Method() と書けます。LINQ の Where や Select なども、IEnumerable に対する拡張メソッドとして実装されており、身近な例といえます。
Q8 : C#で参照型に分類されるものはどれですか?
string は参照型で、オブジェクト本体はヒープ上に確保され、変数にはその参照が格納されます。int と bool はいずれも値型で、DateTime も struct として定義された値型です。string は参照型でありながら不変(イミュータブル)で、== 演算子が参照ではなく文字列の内容を比較するようオーバーロードされている点も特徴です。値型と参照型の違いは、代入時のコピーの挙動やメモリ上の配置に影響するため、C#を学ぶうえで基本となる重要な知識です。
Q9 : async メソッド内で、Task の非同期処理の完了を待機するために使うキーワードはどれですか?
await は async メソッド内で使うキーワードで、Task や Task などの待機可能な処理が完了するまで、スレッドをブロックせずにメソッドの実行を中断します。処理が完了すると、その続きから実行が再開されます。yield は反復子(イテレーター)で値を返すために使い、lock は排他制御のためのキーワードです。defer は C# には存在しません。await を使うことで、非同期コードを同期コードに近い自然な形で書くことができます。
Q10 : 次のコードの意味として正しいものはどれですか?nstring name = input ?? "不明";
?? は null 合体演算子と呼ばれ、左オペランドが null でなければその値を返し、null の場合に限って右オペランドの値を返します。そのため、このコードでは input が null のときだけ name に "不明" が代入されます。空文字列は null ではないので、空文字列の場合はそのまま空文字列が代入される点に注意が必要です。null チェックを簡潔に書けるため、既定値の指定によく使われ、C# 8.0 以降では ??= 演算子も利用できます。
まとめ
いかがでしたか? 今回はC#クイズをお送りしました。
皆さんは何問正解できましたか?
今回はC#クイズを出題しました。
ぜひ、ほかのクイズにも挑戦してみてください!
次回のクイズもお楽しみに。