平々毎々(アーカイブ)

はてなダイアリーのアーカイブです。

「C#できます」の記事は釣りですからね。

第6回日本C#ユーザー会勉強会は延期になってしまったので、想定回答を書いておきます。

注意:決して模範解答ではありませんからね!○×を考えるのはやめてね!そもそも、回答できなきゃC#プログラマじゃないなんてまったく思ってませんから!C#言語仕様外のことを問う設問が多いし!

  1. ==演算子オーバーロードを実装してEqualsメソッドと同じ処理を実行するようにしてもよい場合はどのような時か?
    • (想定回答)同一性を値で判断する、イミュータブルな値オブジェクトの時。(29字)
    • (補足)EqualsのオーバーライドとかGetHashCodeとかIEquatableとかIComparableとか、そういうのの実装も必要に応じていろいろ考えたほうがいいです。
  2. ループ内でなければ、たとえ100個の文字列型変数であってもまとめて+演算子で連結してよい理由を説明せよ。
    • (想定回答)string.Concatを1回呼び出すようにコンパイラが最適化するから。(37字)
    • (補足)JavaでもStringBuilder#appendに最適化されるようだから、あんまり違いはないことに後から気付きました。むしろ、Javaの最適化については仕様に明記されているみたいだけど、C#については明記されていなかった気がするので、この回答は実装依存かも。いずれにせよ、常識的な回数しかループしないとか、短い文字列しか使わないとかなら、最適化とか考えずに好きにすればいいと思います。
  3. Listのように値型を格納するジェネリックコレクションを使ってもボックス化/ボックス化解除が発生しない理由を説明せよ。
    • (想定回答)実行時にそれぞれの値型ごとに特殊化が行われることを前提とした実装だから。(37字)
    • (補足)値型ごとに特殊化というのはCLIの仕様のはず。もしListの実装が内部でobject型を使っていればボックス化されてしまうけど、普通はボックス化されないような実装にするよねえ。
  4. Full GC(Gen2 GC)が動作したときに断片化していてもコンパクションされないヒープ領域はどのような領域か?
    • (想定回答)Large Object Heapと呼ばれる、一定サイズ以上のオブジェクトを格納する領域。(46字)
    • (補足)ピン止めされたオブジェクトもコンパクションでは移動しないと思うんだけど、CLR4から改善されたという情報もありました。MSDNブログのどこかにあったような気がするけどまだ見つけられていないです。
  5. throw; とthrow ex; の違いをスタックトレースの観点で説明せよ。
    • (想定回答)前者はスタックトレースが変化しないが、後者はその位置を起点とするように更新される。(42字)
    • (補足)なんか日本語がおかしいな。
  6. フィールドのアクセス修飾子をprivateにしプロパティのgetter/setterではそのフィールドを読み書きするだけというコードが馬鹿馬鹿しい理由を説明せよ。
    • (想定回答)自動実装プロパティで十分だから。(16字)
    • (補足)自動実装プロパティだとコンパイルの度にバッキングフィールドの名前が変わる可能性があり、シリアライズ・デシリアライズで互換性の問題が発生する恐れがあると『プログラミング .NET Framework』に書いてあります。それ以外でも、何か理由があって自動実装プロパティにしないのであれば別に構わないんです。初期値の問題もあるし、readonlyにできない問題もあるので。「馬鹿馬鹿しい」というのは我ながら煽り過ぎ。
  7. nullを参照している参照型変数のメソッドを呼び出そうとした場合でもNullReferenceExceptionが発生しないのは主にどういう状況か?
    • (想定回答)呼び出そうとしたメソッドが拡張メソッドで、その中でインスタンスの参照を使っていない場合。(45字)
    • (補足)インスタンスメソッドを普通に呼べば(ILのcallvirt命令で)NullReferenceExceptionになるので*1、ずるい問題。「拡張メソッドは参照型変数のメソッドと言えるのか」には、「そもそもインスタンスメソッドも変数のメソッドじゃないよね」と言い返そうかなと思います。すみません。こじつけです。
  8. クラスと構造体の違いは何か?(「スタックとヒープ」以外で)
    • (想定回答)クラスは参照型。構造体は値型。(15字)
    • (補足)違いなんて山ほどあるので、そのどれかを回答していれば正解だと思っています。でも個人的には「値型にはデストラクタや引数なしのコンストラクタを定義できない」とか「値型はlockステートメントで使えない」を挙げたいです。逆にクラスと構造体の共通点として、構造体もインターフェースを実装できることをJavaプログラマは知らないかもしれないですね。インターフェース型の変数に代入するとボックス化されますが。
  9. デストラクタとは何か?
    • (想定回答)C#でobject.Finalizeをオーバーライドするための専用構文。(36字)
    • (補足)「C#にはデストラクタはない。」や「GCが最後に呼び出すメソッド。」には部分点を与えたいなと。C#にはデストラクタ「構文」がありますが、それ自体はメソッドではないですし、C++のデストラクタとは違うものです。
  10. インターフェースの明示的実装を利用する目的を1つ説明せよ。
    • (想定回答)オーバーロードで区別できないが異なるふるまいをするメソッドを実装するため。(38字)
    • (補足)この例ならインターフェースの明示的実装で解決できる部分もあると思います(保守性や拡張性を考えると厳しいけど)。それから、もちろん他にも使いどころはあります。ジェネリックメソッドと非ジェネリックメソッドを用意する(IEnumerable)とか、実装しないメソッドを非公開にする(ReadOnlyCollection)とか*2

*1:yfakariyaからは「非仮想メソッドでもcallvirtを使っているのはMSのC#コンパイラの実装によるもの」と指摘されました

*2:yfakariyaからは「List.GetEnumerator()」も例示されました