どちらがいいですか?一見、オプションのパラメーターの方が優れているように見えますが(コードが少ない、XMLドキュメントが少ないなど)、ほとんどのMSDNライブラリクラスがオプションのパラメーターの代わりにオーバーロードを使用するのはなぜですか?
オプションのパラメータ(またはオーバーロード)を使用する場合に注意しなければならない特別なことはありますか?
どちらがいいですか?一見、オプションのパラメーターの方が優れているように見えますが(コードが少ない、XMLドキュメントが少ないなど)、ほとんどのMSDNライブラリクラスがオプションのパラメーターの代わりにオーバーロードを使用するのはなぜですか?
オプションのパラメータ(またはオーバーロード)を使用する場合に注意しなければならない特別なことはありますか?
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つ以上のメソッドを定義する必要があるため、1つのメソッドが冗長になることに注意してください。私が見つけたこれは、名前付きパラメーターと組み合わせてオプションのパラメーターを使用するための良いユースケースです。
オプションのパラメーターは、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でうまく機能しない場合の、いくつかの追加の読み物があります。
私はそれらが異なる目的を果たしていると信じています。オプションのパラメーターは、パラメーターにデフォルト値を使用できる場合のものであり、基礎となるコードは同じになります。
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) {
...
}
(私はこれについて少し前にここに書きました)
これはほとんど言うまでもありませんが、:
すべての言語がオプションのパラメーターをサポートしているわけではありません。ライブラリをこれらの言語に対応させたい場合は、オーバーロードを使用する必要があります。
確かに、これはほとんどのショップにとっても問題ではありません。しかし、Microsoftが基本クラスライブラリでオプションのパラメータを使用しないのはそのためです。
オプションのパラメータは最後にする必要があります。したがって、オプションでない限り、そのメソッドにパラメータを追加することはできません。元:
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つだけです。
どちらも、もう一方よりも明確に「優れている」わけではありません。どちらも、優れたコードを作成する上での役割を果たします。パラメータにデフォルト値を設定できる場合は、オプションのパラメータを使用する必要があります。メソッドのオーバーロードは、シグニチャの違いがデフォルト値を持つ可能性のあるパラメータを定義しないことを超えている場合に使用する必要があります(たとえば、渡されるパラメータとデフォルトのままになっているパラメータによって動作が異なるなど)。
// 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)
{
...
}
オプションのパラメーターを使用するのに適した場所は、メソッドのオーバーロードをサポートしていないWCFです。
3番目のオプションについてはどうでしょうか。さまざまな「オプションのパラメーター」に対応するプロパティを持つクラスのインスタンスを渡します。
これは、名前付きパラメーターやオプションのパラメーターと同じ利点を提供しますが、多くの場合、これははるかに明確であると感じています。必要に応じて(つまり、構成を使用して)パラメーターを論理的にグループ化し、いくつかの基本的な検証もカプセル化する機会を提供します。
また、メソッドを使用するクライアントが何らかのメタプログラミング(メソッドを含むlinq式の構築など)を実行することを期待している場合は、メソッドのシグネチャを単純に保つことには利点があると思います。
最初の質問に答えるには、
ほとんどのMSDNライブラリクラスがオプションのパラメータの代わりにオーバーロードを使用するのはなぜですか?
下位互換性のためです。
VS2010でC#2、3.0、または3.5プロジェクトを開くと、プロジェクトは自動的にアップグレードされます。
プロジェクトで使用される各オーバーロードを、対応するオプションのパラメーター宣言と一致するように変換する必要がある場合に発生する不便を想像してみてください。
その上、ことわざにあるように、「なぜ壊れていないものを修正するのですか?」。すでに機能しているオーバーロードを新しい実装で置き換える必要はありません。
これは実際には元の質問に対する回答ではなく、@ NileshGuleの回答に対するコメントですが、次のようになります。
a)コメントするのに十分な評判ポイントがありません
b)コメントで複数行のコードを読むのは非常に難しい
NileshGuleは次のように書いています。
オプションのパラメーターを使用する利点の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文字列参照を指定した場合、デフォルト値に置き換えられません。したがって、入力パラメータでnullをチェックする必要があります。
オプションのパラメーターを使用する利点の1つは、入力パラメーターの1つが文字列である場合に、文字列がnullであるか空であるかなど、メソッドで条件付きチェックを行う必要がないことです。オプションのパラメータにはデフォルト値が割り当てられるため、防御コーディングは大幅に削減されます。
名前付きパラメーターは、パラメーター値を任意の順序で渡す柔軟性を提供します。