web-dev-qa-db-ja.com

メソッドのオーバーロードとC#4.0のオプションパラメーター

どちらの方がよいですか?一見、オプションのパラメーターはより良いように見えます(コードが少なく、XMLドキュメントが少ないなど)が、ほとんどのMSDNライブラリクラスがオプションのパラメーターの代わりにオーバーロードを使用するのはなぜですか?

オプションのパラメーター(またはオーバーロード)を使用する場合に注意する必要がある特別なことはありますか?

114
Louis Rhys

C#4.0の「名前付きパラメーター」と組み合わせた「オプションパラメーター」の良い使用例の1つは、パラメーターの数に基づいてメソッドをオーバーロードするメソッドオーバーロードのエレガントな代替手段を提供することです。

たとえば、メソッドfooを次のように呼び出し/使用する場合、foo()foo(1)foo(1,2)foo(1,2, "hello")。メソッドのオーバーロードを使用すると、このようなソリューションを実装できます。

///Base foo method
public void DoFoo(int a, long b, string c)
{
   //Do something
}  

/// Foo with 2 params only
public void DoFoo(int a, long b)
{
    /// ....
    DoFoo(a, b, "Hello");
}

public void DoFoo(int a)
{
    ///....
    DoFoo(a, 23, "Hello");
}

.....

C#4.0のオプションパラメータを使用すると、次のようなユースケースを実装できます。

public void DoFoo(int a = 10, long b = 23, string c = "Hello")

次に、そのようなメソッドを使用できます-名前付きパラメーターの使用に注意してください-

DoFoo(c:"Hello There, John Doe")

この呼び出しは、パラメーターaの値を10として、パラメーターbを23として受け取ります。この呼び出しの別のバリエーション-メソッドに表示される順序でパラメーター値を設定する必要がないことに注意してください署名、名前付きパラメーターは値を明示的にします。

DoFoo(c:"hello again", a:100) 

名前付きパラメーターを使用する別の利点は、オプションのパラメーターメソッドの可読性を向上させ、コードのメンテナンスを大幅に強化することです。

1つのメソッドが、メソッドのオーバーロードで3つ以上のメソッドを定義しなければならないことで、冗長性が非常に高くなることに注意してください。これは、オプションのパラメーターを名前付きパラメーターと組み合わせて使用​​するのに適したユースケースです。

77
Bikal Lem

オプションのパラメーターは、APIとして公開するときに問題を提供します。パラメータの名前を変更すると、問題が発生する可能性があります。デフォルト値を変更すると問題が発生します(情報については、たとえばこちらを参照してください: C#4.0オプションパラメータの警告

また、オプションのparamsはコンパイル時の定数にのみ使用できます。これを比較してください:

public static void Foo(IEnumerable<string> items = new List<string>()) {}
// Default parameter value for 'items' must be a compile-time constant

これに

public static void Foo() { Foo(new List<string>());}
public static void Foo(IEnumerable<string> items) {}
//all good

更新

デフォルトパラメータを持つコンストラクタが Reflectionでうまく再生されない の場合の追加の参考資料を次に示します。

57
flq

それらは異なる目的に役立つと思います。オプションのパラメーターは、パラメーターにデフォルト値を使用できる場合に使用します。基礎となるコードは同じです。

public CreditScore CheckCredit( 
  bool useHistoricalData = false,  
  bool useStrongHeuristics = true) { 
  // ... 
}

メソッドのオーバーロードは、相互に排他的な(のサブセット)パラメーターがある場合に使用します。これは通常、いくつかのパラメーターを前処理する必要があること、またはメソッドのさまざまな「バージョン」に対してまったく異なるコードがあることを意味します(この場合でも一部のパラメーターを共有できるため、上記の「サブセット」に言及した理由です) :

public void SendSurvey(IList<Customer> customers, int surveyKey) {  
  // will loop and call the other one 
} 
public void SendSurvey(Customer customer, int surveyKey) {  
  ...  
}

(これについては少し前に書きました here

37
Jordão

これはほとんど言うまでもありませんが、次のとおりです。

すべての言語がオプションのパラメーターをサポートしているわけではありません。ライブラリをこれらの言語に対応させたい場合は、オーバーロードを使用する必要があります。

確かに、これはほとんどの店にとっても問題ではありません。しかし、Microsoftが基本クラスライブラリでオプションのパラメーターを使用しないのは、このためです。

27
Joe White

どちらも他のものより決定的に「優れた」ものではありません。彼らは両方とも良いコードを書くのに彼らの場所を持っています。パラメータにデフォルト値を設定できる場合は、オプションのパラメータを使用する必要があります。メソッドのオーバーロードは、シグネチャの違いがデフォルト値を持つ可能性のあるパラメーターを定義しないことを超える場合に使用する必要があります(たとえば、どのパラメーターが渡され、どのパラメーターがデフォルトのままになるかによって動作が異なります)。

// this is a good candidate for optional parameters
public void DoSomething(int requiredThing, int nextThing = 12, int lastThing = 0)

// this is not, because it should be one or the other, but not both
public void DoSomething(Stream streamData = null, string stringData = null)

// these are good candidates for overloading
public void DoSomething(Stream data)
public void DoSomething(string data)

// these are no longer good candidates for overloading
public void DoSomething(int firstThing)
{
    DoSomething(firstThing, 12);
}
public void DoSomething(int firstThing, int nextThing)
{
    DoSomething(firstThing, nextThing, 0);
}
public void DoSomething(int firstThing, int nextThing, int lastThing)
{
    ...
}
9
Michael Meadows

オプションのパラメータは最後でなければなりません。そのため、オプションでもない限り、そのメソッドに追加のパラメーターを追加することはできません。例:

void MyMethod(int value, int otherValue = 0);

オーバーロードせずにこのメソッドに新しいパラメーターを追加する場合は、オプションである必要があります。このような

void MyMethod(int value, int otherValue = 0, int newParam = 0);

オプションにできない場合は、オーバーロードを使用して、「otherValue」のオプション値を削除する必要があります。このような:

void MyMethod(int value, int otherValue = 0);
void MyMethod(int value, int otherValue, int newParam);

パラメータの順序を同じに保ちたいと思います。

そのため、オプションのパラメータを使用すると、クラスに必要なメソッドの数は減りますが、最後にする必要があるという点で制限されます。

更新オプションのパラメーターでメソッドを呼び出す場合、次のような名前付きパラメーターを使用できます。

void MyMethod(int value, int otherValue = 0, int newValue = 0);

MyMethod(10, newValue: 10); // Here I omitted the otherValue parameter that defaults to 0

したがって、オプションのパラメーターを使用すると、呼び出し元により多くの可能性が与えられます。

最後に一つだけ。次のように、1つの実装でメソッドのオーバーロードを使用する場合:

void MyMethod(int value, int otherValue)
{
   // Do the work
}

void MyMethod(int value)
{
   MyMethod(value, 0); // Do the defaulting by method overloading
}

次に、このように「MyMethod」を呼び出す場合:

MyMethod(100); 

2つのメソッド呼び出しが発生します。ただし、オプションのパラメーターを使用する場合、「MyMethod」の実装は1つだけなので、メソッド呼び出しは1つだけです。

オプションのパラメーターを使用するのに適した場所は[〜#〜] wcf [〜#〜]です。メソッドのオーバーロードをサポートしていないためです。

4
ashraf

これは実際には元の質問に対する答えではなく、@ NileshGuleの answer に対するコメントですが、次のとおりです。

a)コメントするのに十分な評判ポイントがありません

b)複数行のコードはコメントで読むのが非常に難しい

Nilesh Guleの投稿:

オプションパラメータを使用する利点の1つは、入力パラメータの1つが文字列である場合に文字列がnullまたは空だった場合など、メソッドで条件付きチェックを実行する必要がないことです。オプションのパラメータにデフォルト値が割り当てられるため、防御的なコーディングが大幅に削減されます。

これは実際には正しくありません。nullを確認する必要があります。

void DoSomething(string value = "") // Unfortunately string.Empty is not a compile-time constant and cannot be used as default value
{
  if(value == null)
    throw new ArgumentNullException();
}

DoSomething(); // OK, will use default value of ""
DoSomething(null); // Will throw

Null文字列参照を指定すると、デフォルト値に置き換えられません。そのため、入力パラメーターにヌルがないかどうかを確認する必要があります。

2
Anlo

3番目のオプションはどうですか:さまざまな「オプションのパラメーター」に対応するプロパティを持つクラスのインスタンスを渡します。

これは、名前付きパラメーターおよびオプションのパラメーターと同じ利点を提供しますが、これは多くの場合より明確だと感じています。必要に応じて(つまり、構成を使用して)パラメーターを論理的にグループ化し、基本的な検証もカプセル化する機会を提供します。

また、メソッドを使用するクライアントがあらゆる種類のメタプログラミング(メソッドを含むlinq式の構築など)を行うことを期待する場合、メソッドシグネチャをシンプルに保つことには利点があると思います。

2
Michael Petito

最初の質問に対処するには、

ほとんどのMSDNライブラリクラスがオプションのパラメーターの代わりにオーバーロードを使用するのはなぜですか?

下位互換性のためです。

VS2010でC#2、3.0、または3.5プロジェクトを開くと、自動的にアップグレードされます。

プロジェクトで使用される各オーバーロードを、対応するオプションのパラメーター宣言と一致するように変換する必要がある場合に生じる不便を想像してください。

その上、sayingにあるように、「なぜ壊れていないものを修正するのですか?」。新しい実装で既に機能しているオーバーロードを置き換える必要はありません。

1
Alex Essilfie

オプションパラメータを使用する利点の1つは、入力パラメータの1つが文字列である場合に文字列がnullまたは空だった場合など、メソッドで条件付きチェックを実行する必要がないことです。オプションのパラメータにデフォルト値が割り当てられるため、防御的なコーディングが大幅に削減されます。

名前付きパラメーターを使用すると、パラメーター値を任意の順序で渡すことができます。

1
Nilesh Gule