trsing’s diary

勉強、読んだ本、仕事で調べたこととかのメモ。

EFFECTIVE C# 6.0/7.0 読書メモ 項目24

24 親クラスやインターフェイス用に特化したジェネリックメソッドを作成しないこと

オーバーロード解決について。 ジェネリックメソッドは、型パラメータをそれぞれに該当するあらゆる型と一致できる。暗黙的型変換(例えば親クラスへの変換)よりも優先されるため、使用する側にとってわかりにくくなる場合がある。使用する側が混乱しないように実装すべき。

オーバーロード解決のルール

一致度が高いものが優先される

ufcpp.net

引数の型

引数の型は、以下のリストの上の方ほど「一致度が高い」と判断されます。

  • ぴったり一致する型
  • ジェネリックな型
  • 親クラス
    • 多段に派生している場合、近い方ほど優先
  • 暗黙的に変換できる型
  • object

public class MyBase{}
public interface IMessageWrite{/*略*/}
public class MyDerived : MyBase, IMessageWrite{/*略*/}
public class AnotherType : IMessageWriter{/*略*/}

static void WriteMessage(MyBase b){/*略*/}
static void WriteMessage<T>(T obj){/*略*/}
static void WriteMessage(IMessageWriter obj){/*略*/}

MyDerived d = new MyDerived();
WriteMessage(d);                        //WriteMessage<T>(T) MyDerived
WriteMessage((IMessageWriter)d);        //IMessageWriter MyDerived
WriteMessage((MyBase)d);                //WriteMessage(MyBase) MyDerived
AnotherType anObject = new AnotherType();
WriteMessage(abObject);                 //WriteMessage<T>(T) AnotherType
WriteMessage((IMessageWriter)anObject); //IMessageWriter AnotherType

MyDerivedに厳密に一致するのはジェネリックメソッド。 親クラスであるMyBaseには暗黙的型変換が必要なため優先度が下がる。 明示的な変換を行えば変換後のものが厳密に一致となる。

static void WriteMessage<T>(T obj)コメントアウトすると WriteMessage(d);でエラーになる

次のメソッドまたはプロパティ間で呼び出しが不適切です: 'Program.WriteMessage(MyBase)' と'Program.WriteMessage(IMessageWriter)'

親クラスの方が優先順位高いはずだけど…わからん。

ジェネリックメソッド作成時の注意点

  • 次の理由から、型のチェックは実行時に行うのではなく、コンパイルに処理させた方が良い
    • チェックする条件が少ない間は良いが、増えてくるとコードが煩雑になる
    • 実行時にオーバーヘッドがかかる
  • 実行時の判断が明らかに適切な場合に採用すべき
    • 採用する際は、問題が起こらないようなライブラリを作成できるかパフォーマンスも含めて検討する。
  • 特定の型/インターフェイスに対するジェネリックメソッドを作成する場合、その型ならびに派生クラスすべて/そのインターフェイスを実装するすべての型に対するメソッドを用意する