web-dev-qa-db-ja.com

パラメータアクション<T1、T2、T3>。T3はオプションです。

私は次のコードを持っています:

public static MyMethod()  
{ 
   ...Do something  
   ProtectedMethod(param1, param2);  
   ...Do something  
}  

protected static void ProtectedMethod(IEnumerable<string> param1, string param2, int param3 = 1)  
{  
   ... Do something  
}

オプションのparam3パラメーターに注意してください。

かなりの理由で、MyMethodメソッドのコードを独自のクラスに抽出する必要がありますが、このクラスから継承しているすべてのクラスのためにProtectedMethodを抽出できず、変更を小さく分離しておく必要があります。そのため、ProtectedMethodと同じ署名を持つ新しいクラスにAction <>デリゲートを含めることができると考えました。

問題は、デリゲートを次のように宣言した場合です。

protected readonly Action<IEnumerable<string>, string, int> m_ProtectedMethod;

抽出されたコードは、メソッドが2つのパラメーターでのみ呼び出されていることを示しているため、これを好みません。

そして、私がそのようにデリゲートを宣言した場合:

protected readonly Action<IEnumerable<string>, string> m_ProtectedMethod;

それをパラメーターとして新しいクラスに送信すると、メソッドが2つではなく3つのパラメーターを持つように定義されているため、それも気に入らない。

これまでのところ、これを解決するために私が考えた唯一の方法は、オーバーロードされたバージョンのProtectedMethodを作成して、オプションのパラメーターを削除することです。

これが唯一のオプションですか、それとも、オーバーロードされたメソッドの代わりにオプションのパラメーターを使用することが推奨されるため、別の方法がありますか?

30
Sergio Romero

オプションのパラメーターは、メソッドまたはデリゲートパラメーターの属性です。コンパイル時に既知のオプションのパラメーターを持つシグニチャー(メソッドまたはデリゲート)を呼び出すと、コンパイラーはオプションのパラメーター値を呼び出しサイトに挿入します。

ランタイムはオプションのパラメーターを認識しないため、呼び出されたときにオプションのパラメーターを挿入するデリゲートを作成することはできません。

代わりに、オプションのパラメーターを使用してカスタムデリゲート型を宣言する必要があります。

public delegate void MyDelegate(IEnumerable<string> param1, string param2, int param3 = 1);

このデリゲートを呼び出すときは、含まれているメソッドの宣言に関係なく、3番目のパラメーターを省略できます。

30
SLaks

それはどのようにm_ProtectedMethodは消費されますが、自分の状況で妥協点を見つけました。つまり、一方のオーバーロードをもう一方よりも多く使用します。

単純により単純な(ジェネリックパラメーターが少ない)Action <>変数を定義します、より複雑な提供されたAction変数メソッドを呼び出します。これは、(i)使用時のローカルスコープのいずれかで実行できます。または(ii)アクションプロパティまたはオブジェクト構築の割り当て時のオブジェクトスコープ。

変数/プロパティのオーバーロードなどはないため、結果として得られる2つの関連するアクション変数には2つの異なる名前が必要です。

EG i:ローカルスコープ(おそらくあなたのシナリオには最適ではありません)

public MyMethod(Action<IEnumerable<string>, string, int> m_ProtectedMethod2)  
{ 
   Action<IEnumerable<string>, string> m_ProtectedMethod = (p1,p2) => {
      m_ProtectedMethod2(p1,p2,1); //The value 1 is the default 3rd parameter
   }

   ...Do something  
   m_ProtectedMethod(param1, param2);  
   ...Do something  
   ...If something  
      m_ProtectedMethod2(param1, param2, param3); //Calling the more complex form directly
   ...Do something  
}  

EG ii:オブジェクトスコープ

private Action<IEnumerable<string>, string, int> m_ProtectedMethod2 = null;
private Action<IEnumerable<string>, string> m_ProtectedMethod = null;
protected Action<IEnumerable<string>, string, int> ProtectedMethod
{
   get { return m_ProtectedMethod2; }
   set {
      m_ProtectedMethod2 = value;
      m_ProtectedMethod = (p1,p2) => {
         m_ProtectedMethod2(p1,p2,1); //The value 1 is the default 3rd parameter
      }
   }
}

public MyMethod()
{
   ...Do something  
   m_ProtectedMethod(param1, param2);  
   ...Do something  
   ...If something  
      m_ProtectedMethod2(param1, param2, param3); //Calling the more complex form directly
   ...Do something  
}

どちらの場合も、デフォルト設定値をより扱いにくい名前の変数に設計し、2のサフィックスを付けて、消費時に単純なオーバーロードがより基本的な変数名を持つようにしたことに注意してください。

1
Todd

(デリゲート指向の)戦略パターンと混合されたオーバーロードのよりエレガントな実装であると私が見つけたもので他の人を助けることを望んでいます。

public class OverloadExample {
    private Action<int, bool> _implementation;

    public OverloadExample() {
        _implementation = defaultImplementation;
    }

    public OverloadExample(Action<int, bool> implementation) {
        _implementation = implementation;
    }

    protected void defaultImplementation(int aInt, bool aBool) {
        //
    }

    public void Implementation(int someInt, bool someBool = true) {
        _implementation(someInt, someBool);
    }
}

使用法:

new OverloadExample().Implementation(9001);
new OverloadExample().Implementation(9001, false);
0
Caleb Gray