2011年1月16日日曜日

C# のアンチイディオム

ソフトウェア開発の様々なテーマについて、それぞれパターンやイディオムやベストプラクティスがあるけど、まずはアンチパターンを避ける事が優先度が上で順序も先だと常々思う。

というわけで、書いてはいけない C#コードを『Effective C#』と『More Effective C#』から抜粋してみた("m"が付いた番号が "More"の方)。

Item 09: Avoid Conversion Operators in Your APIs
  • 変換演算子の定義は避ける事。

Item 26: Avoid Returning References to Internal Class Objects
  • オブジェクトが保持している別のオブジェクトを外に晒すのは避ける事
  • 特にコレクションなんかで、C#に限らず散々言われ続けてきた悪いコーディング

Item 16: Avoid Creating Unnecessary Objects
  • 無駄なオブジェクトの生成を避ける事
  • 文字列の構築での string の使用 ⇒ StringBuilder を使う事
  • 高頻度で呼ばれるメソッド内でのオブジェクト生成 ⇒ メンバ変数や定数への格上げを検討する事

Item 32: Avoid ICloneable
  • ICloneableの実装は避ける事
  • パッと見の印象ほどには意外と使えないインターフェイスだったりする
  • どうしても使うときは、基底クラスでコピーコンストラクタを定義した上で、sealed クラスのみで使う事。

Item 33: Use the new Modifier Only to React to Base Class Updates
  • new モディファイアの使用は避ける事
  • 外部のライブラリに含まれる基底クラスの変更により、それを継承している派生クラスで定義の衝突が生じるような稀なケースなどもあるが、そのときでもなるべく避ける事。

Item 34: Avoid Overloading Methods Defined in Base Classes
  • 基底クラスで宣言されたメソッドをオーバーロードするのは避ける事
  • こんなコードで、、、
    class B1 {}
    class D1: B1 {}
    class B2 { void Foo(D1 p) {...} }
    class D2: B2 { void Foo(B1 p) {...} }
    、、、D2.Foo() に D1 オブジェクトを渡すと、どっちが実行されるか意外と迷う。更に generics が絡むともっと面倒になる。

Item m07: Do Not Create Generic Specialization on Base Classes or Interfaces
  • generic メソッドを特化するオーバロードは避ける事
  • 例えば以下のようなコードで、、、
    class Base {}
    class Derived : Base {}
    class Foo {
    public static void DoSomething(Base b) { ... }
    public static void DoSomething(T b) { ... }
    }
    、、、DoSomething() に Derived インスタンスを渡すとどちらが実行されるか、意外と分かりにくい。DoSomething(Base b)を加える代わりに DoSomething(T b)の中で型チェックをすると、分かり易さも使い勝手も向上する。

Item m33: Avoid Modifying Bound Variables
  • クロージャの中でのローカル変数の変更は避ける事
  • 遅延実行絡みの分かりにくいエラーが生じやすい

Item m35: Never Overload Extension Methods
  • 拡張メソッドのオーバーロードは避ける事
  • 別々の namespace に異なる実装を書くと、拡張メソッドをオーバーロードした感じになるが、拡張メソッドの使い方としては間違い
  • 別の識別子で別途定義すべき

Item m39: Avoid Throwing Exceptions in Functions and Actions
  • シーケンスを操作する LINQ コードの中での例外送出は避ける事

Item m41: Avoid Capturing Expensive Resources
  • クロージャがリソースをつかみっぱなしになるのを避ける事
  • クロージャが、IDisposable なローカル変数を参照するコードを書くと、クロージャの生存期間や遅延実行がからんで面倒な事になりがち

Item m48: Avoid Calling Virtual Functions in Constructors
  • コンストラクタで仮想関数を呼ばない事

Item m15: Avoid Calling Unknown Code in Locked Sections
  • 未知のコードを lock ブロックの中で実行しない事
  • 例えば、パラメータで渡されたりイベントに追加されたデリゲートや、オーバーライドされた仮想メソッドなどが、デッドロックを発生させる事がある

Item m24: Declare Only Nonvirtual Events
  • virtual なイベントは避けること
  • ほとんど意味が無く誰も得しない

Item m47: Limit Array Parameters to Params Arrays
  • 可変引数以外では、配列を受け取るメソッドシグネーチャは避ける事
  • covariance のせいで実行時に面倒くさいエラーが起こりがち
  • シーケンスを渡すときは配列ではなく、IEnumerable を使う
  • 中身を変更するときはIEnumerable を返す

中には FxCop とかで検出可能なものもあるらしいが、そうでないものも含めてコーディング規約とかガイドラインにも載せて、開発メンバで共有できるようにしたい。

0 件のコメント:

コメントを投稿