必須ではありませんが、オプションのパラメーター名を指定しますか?


25

次の方法を検討してください。

public List<Guid> ReturnEmployeeIds(bool includeManagement = false)
{

}

そして、次の呼び出し:

var ids = ReturnEmployeeIds(true);

システムを初めて使用する開発者にとって、何trueが起こったのかを推測するのはかなり難しいでしょう。最初にやるべきことは、メソッド名にカーソルを合わせるか、定義に移動することです(どちらも少しでも大きなタスクではありません)。しかし、読みやすくするために、次のように書くのは理にかなっています。

var ids = ReturnEmployeeIds(includeManagement: true);

コンパイラが必要としないときにオプションのパラメータを明示的に指定するかどうかを正式に議論している場所はどこにありますか?

次の記事では、特定のコーディング規則について説明しています。https//msdn.microsoft.com/en-gb/library/ff926074.aspx

上記の記事に似た何かが素晴らしいでしょう。


2
これらは、2つのやや無関係な質問です。(2)booleanメソッド引数を使用する必要がありますか?
キリアンフォス

5
これはすべて、個人的な好み/スタイル/意見に帰着します。個人的には、渡された値がリテラルである場合、パラメーター名が好きです(変数は、その名前を通じて十分な情報を既に伝えている可能性があります)。しかし、それは私の個人的な意見です。
メタファイト

1
そのリンクを質問のソースの例として含めてください。
メタファイト

4
それが何かを追加する場合、私はそれをStyleCopとReSharperで実行しました-どちらも明確な見方を持っていませんでした。ReSharperは、単に名前付きパラメーター削除できると言ってから、名前付きパラメーター追加できると続けます。私が推測する理由のためにアドバイスは無料です...:-/
ロビーディー

回答:


28

C#の世界では、enumはここでの最良の選択肢の1つになると思います。

それを行うと、あなたは何をしているのかを説明することを余儀なくされ、すべてのユーザーは何が起こっているのかを見ることになります。また、将来の拡張に対しても安全です。

public enum WhatToInclude
{
    IncludeAll,
    ExcludeManagement
}

var ids = ReturnEmployeeIds(WhatToInclude.ExcludeManagement);

だから、私の意見ではここに:

列挙型>オプションの列挙型>オプションのbool

編集:[Flags]この特定の場合に適している可能性のある列挙型に関する下のLeopardSkinPillBoxHatとの議論のため(具体的には物事の包含/除外について話しているため)、代わりにISet<WhatToInclude>パラメータとして使用することを提案します。

これは、いくつかの利点を備えた、より「現代的な」概念であり、主にLINQファミリーのコレクションに収まるという事実に由来し[Flags]ますが、最大32グループを持っています。使用の主な欠点はISet<WhatToInclude>、C#構文でサポートされるセットの質の低さです。

var ids = ReturnEmployeeIds(
    new HashSet<WhatToInclude>(new []{ 
        WhatToInclude.Cleaners, 
        WhatToInclude.Managers});

セットの生成と次のような「ショートカットセット」の作成の両方のために、ヘルパー関数によって軽減される場合があります。

var ids = ReturnEmployeeIds(WhatToIncludeHelper.IncludeAll)

私は同意する傾向があります。将来的に条件が拡張される可能性が漠然とある場合は、通常、新しいコードで列挙型を使用します。何かがトライステートになったときにboolをenumに置き換えると、本当にノイズの多いdiffが作成され、非難コードがずっと面倒になります。第3のオプションの場合、1年後にコードを改修しなければならなくなる場合。
ダン・ニーリー

3
私のようなenumアプローチが、私は混合の巨大なファンではないIncludeExclude同じでenum、それは何が起こっているのかを把握することはより困難だとして。これを本当に拡張可能にしたい場合は、それをa [Flags] enumにしてIncludeXxx、すべてを作成し、にIncludeAll設定されたを提供できますInt32.MaxValue。次に、2つのReturnEmployeeIdsオーバーロードを提供できます。1つはすべての従業員を返し、1つWhatToIncludeは列挙型フラグの組み合わせであるフィルターパラメーターを受け取ります。
LeopardSkinPillBoxHat

@LeopardSkinPillBoxHat OK、除外/含めることに同意します。したがって、これは少し不自然な例ですが、IncludeAllButManagementと呼ぶこともできます。あなたの他の点では、私は同意しません-私[Flags]は遺物であり、レガシーまたはパフォーマンス上の理由でのみ使用されるべきだと思います。a ISet<WhatToInclude>は非常に優れた概念であると思います(残念ながら、C#には使いやすくするための構文糖が欠けています)。
NiklasJ

@NiklasJ-私はこのFlagsアプローチが好きです。なぜなら、呼び出し元がWhatToInclude.Managers | WhatToInclude.Cleanersビット単位でパスできるか、呼び出し元がそれをどのように処理するかを正確に表現するからです(つまり、マネージャーやクリーナー)。直感的な構文糖:
LeopardSkinPillBoxHat

@LeopardSkinPillBoxHatそのとおりですが、それがこのメソッドの唯一の利点です。欠点には、たとえば、選択肢の数が限られており、他のlinqのような概念と互換性がないことが含まれます。
NiklasJ

20

書くのは理にかなっていますか:

 var ids=ReturnEmployeeIds(includeManagement: true);

これが「良いスタイル」であるかどうかは議論の余地がありますが、私見では、少なくとも悪いスタイルではなく、コード行が「長くなりすぎない」限り有用です。名前付きパラメーターがない場合、説明変数を導入することも一般的なスタイルです。

 bool includeManagement=true;
 var ids=ReturnEmployeeIds(includeManagement);

名前付きパラメーターはバージョン4.0以前の言語の一部ではなかったため、このスタイルはおそらく名前付きパラメーターバリアントよりもC#プログラマーの間で一般的です。可読性への影響はほぼ同じで、追加のコード行が必要です。

だから私の推奨事項:enumまたは異なる名前の2つの関数の使用が「過剰」だと思う場合、または異なる理由で署名を変更したくない、または変更できない場合は、名前付きパラメーターを使用してください。


2番目の例
アルバロ

10
@Alvaroこのような些細な場合(変数の値が一定である場合)に当てはまる可能性は非常に低いです。たとえそうであっても、言及する価値はほとんどありません。私たちのほとんどは、最近4KBのRAMで実行していません。
クリスヘイズ

3
これはC#であるためincludeManagement、ローカルとしてマークし、const追加メモリをまったく使用しないようにすることができます。
ジェイコブクラル

8
皆さん、私はここで重要だと思うものだけをそらすかもしれないものを私の答えに追加しません。
ドックブラウン

17

次のようなコードが発生した場合:

public List<Guid> ReturnEmployeeIds(bool includeManagement = false)
{

}

次の{}ような内容になることを保証できます。

public List<Guid> ReturnEmployeeIds(bool includeManagement = false)
{
    if (includeManagement)
        return something
    else
        return something else
}

言い換えると、ブール値は2つのアクションコースから選択するために使用されます。メソッドには2つの責任があります。これは、コードの匂いがほとんど保証されています。メソッドが1つのことだけを行うように、常に努力する必要があります。責任は1つだけです。したがって、私はあなたの解決策の両方が間違ったアプローチであると主張します。オプションのパラメータを使用することは、メソッドが複数のことを実行していることを示しています。ブールパラメータもこの兆候です。両方を使用すると、アラームのベルが鳴るように設定する必要があります。したがって、2つの異なる機能セットを2つの別々のメソッドにリファクタリングする必要があります。メソッドが何をするのかを明確にする名前を付けてください。例:

public List<Guid> GetNonManagementEmployeeIds()
{

}

public List<Guid> GetAllEmployeeIds()
{

}

これにより、コードの可読性が向上し、SOLIDの原則に従うことが確実になります。

編集 コメントで指摘されているように、ここでは、これらの2つのメソッドは機能を共有する可能性が高く、共有機能は、機能の一部を制御するブールパラメータを必要とするプライベート関数にラップするのが最適です。

この場合、コンパイラーが必要としない場合でも、パラメーター名を指定する必要がありますか?それに対する簡単な答えはないことをお勧めします。その判断はケースバイケースで必要です。

  • 名前を指定するための引数は、他の開発者(6か月後の自分を含む)がコードの主な対象者であるべきだということです。コンパイラーは間違いなく二次的な聴衆です。そのため、他の人が読みやすいようにコードを常に書いてください。コンパイラが必要とするものだけを提供するのではなく。このルールを毎日使用する方法の例は、変数_1、_2などでコードを埋めず、意味のある名前を使用することです(コンパイラーにとって意味がない)。
  • 反論は、プライベートメソッドは実装の詳細であるということです。以下のようなメソッドを見る人は誰でも、コードの内部にあるブール値パラメーターの目的を理解するためにコードをトレースすることが合理的に期待できます。
public List<Guid> GetNonManagementEmployeeIds()
{
    return ReturnEmployeeIds(true);
}

ソースファイルとメソッドは小さく、理解しやすいですか?はいの場合、名前付きパラメーターはおそらくノイズになります。いいえの場合は、非常に役立ちます。したがって、状況に応じてルールを適用してください。


12
私はあなたが言っていることを聞きますが、あなたは私が尋ねていることのポイントを逃したようなものです。オプションのパラメーターを使用するの最も適切なシナリオ(これらのシナリオは存在します)を想定すると、パラメーターが不要であっても名前を付けても大丈夫ですか?
JᴀʏMᴇᴇ

4
すべて真実ですが、問題はそのようなメソッドの呼び出しについてでした。また、フラグパラメーターは、メソッド内の分岐をまったく保証しません。単純に別のレイヤーに渡される場合があります。
ロビーディー

これは間違っていませんが、単純化しすぎています。実際のコードでは、が見つかるpublic List<Guid> ReturnEmployeeIds(bool includeManagement) { calculateSomethingHereForBothBranches; if (includeManagement) return something else return something else }ので、単純に2つのメソッドに分割することは、おそらくこれら2つのメソッドが最初の計算の結果をパラメーターとして必要とすることを意味します。
ドックブラウン

4
私たちが言っていることは、クラスのパブリックフェイスはおそらく2つのメソッド(それぞれ1つの責任を持つ)を公開するはずですが、内部的には、これらのメソッドはそれぞれ、boolパラメータを分岐する単一の プライベートメソッドにリクエストを合理的に転送できると思います。
メタファイト

1
2つの機能間で動作が異なる3つのケースを想像できます。1.異なる前処理があります。パブリック関数が引数をプライベート関数に前処理するようにします。2.後処理が異なります。パブリック関数にプライベート関数からの結果を後処理させる。3.異なる中間動作があります。これは、言語がサポートしていると仮定して、ラムダとしてプライベート関数に渡すことができます。多くの場合、これにより、以前は実際には発生しなかった新しい再利用可能な抽象化が実現します。
jpmc26

1

システムを初めて使用する開発者にとって、本当のことを推測するのはかなり難しいでしょう。最初にやるべきことは、メソッド名にカーソルを合わせるか、定義に移動することです(どちらも少しでも大きなタスクではありません)

そうですね。自己文書化コードは美しいものです。他の答えが指摘しているようにReturnEmployeeIds、enum、異なるメソッド名などを使用して、呼び出しのあいまいさを取り除く方法があります。それらはどこでも(Visual Basicの冗長性が好きでない限り)。

たとえば、1つの呼び出しを明確にするのに役立つかもしれませんが、必ずしも別の呼び出しではありません。

ここで名前付き引数が役立つ場合があります。

var ids = ReturnEmployeeIds(includeManagement: true);

追加の明確さは追加しません(これは列挙型を解析していると推測します):

Enum.Parse(typeof(StringComparison), "Ordinal", ignoreCase: true);

それは実際に明確性を低下させる可能性があります(人がリストの容量を理解していない場合):

var employeeIds = new List<int>(capacity: 24);

または、適切な変数名を使用しているため、重要ではない場所:

bool includeManagement = true;
var ids = ReturnEmployeeIds(includeManagement);

コンパイラが必要としないときにオプションのパラメータを明示的に指定するかどうかを正式に議論している場所はどこにありますか?

知らない。ただし、両方の部分に対処しましょう。名前付きパラメーターを明示的に使用する必要があるのは、コンパイラーがそうする必要があるためです(通常、ステートメントのあいまいさを取り除くためです)。それ以外は、いつでも好きなときに使用できます。MSからのこの記事では、あなたがそれらを使用することができますときにいくつかの提案を持っていますが、それは特に決定的ではありませんhttps://msdn.microsoft.com/en-us/library/dd264739.aspx

個人的には、カスタム属性を作成したり、パラメーターを変更したり並べ替えたりできる新しいメソッドをスタブするときに最も頻繁に使用します(b / c名前付き属性は任意の順序でリストできます)。さらに、静的な値をインラインで提供する場合にのみ使用します。そうでない場合は、変数を渡す場合、適切な変数名を使用しようとします。

TL; DR

最終的には個人の好みに帰着します。もちろん、名前付きパラメーターを使用する必要があるいくつかのユースケースがありますが、その後、判断の呼び出しを行う必要があります。IMO-コードの文書化、あいまいさの軽減、またはメソッドシグネチャの保護に役立つと思われる場所で使用します。


0

パラメータは、メソッドの(完全修飾)名から明らかであり、それの存在は、メソッドが行うことになっているものに自然であるならば、あなたがすべきではないというパラメータの構文を使用します。たとえば、ReturnEmployeeName(57)それ57が従業員のIDであることは非常に明白なので、名前付きパラメーター構文で注釈を付けることは冗長です。

ではReturnEmployeeIds(true)、あなたが言ったように、関数の宣言/ドキュメントを見ていない限り、それはどのような数字に不可能だtrue手段-あなたがそうすべきという名前のパラメータの構文を使用します。

次に、この3番目のケースを考えます。

void PrintEmployeeNames(bool includeManagement) {
    foreach (id; ReturnEmployeeIds(includeManagement)) {
        Console.WriteLine(ReturnEmployeeName(id));
    }
}

名前付きパラメーター構文はここでは使用されませんが、変数をReturnEmployeeIdsに渡しているため、その変数には名前があり、その名前はたまたまパラメーターに渡したものと同じことを意味します(多くの場合-常にではありません!)-コードから意味を理解するために名前付きパラメーター構文は必要ありません。

そのため、ルールは単純です。メソッド呼び出しからパラメーターの意味を簡単に理解できる場合は、名前付きパラメーターを使用しないでください。できない場合-それはコードの可読性の欠陥であり、おそらくそれらを修正する必要があります(もちろん価格ではありませんが、通常はコードを読みやすくする必要があります)。修正の名前はパラメーターである必要はありません-最終結果がパラメーターの機能を理解しやすくする限り、最初に変数に値を入れるか、他の回答で提案された方法を使用することもできます。


7
ReturnEmployeeName(57)それ57がIDであることをすぐに明らかにすることに同意しません。GetEmployeeById(57)一方、...
ヒューゴZinkの

@HugoZink最初はこの例のようなものを使用したかったのですReturnEmployeeNameが、メソッドの名前でパラメーターを明示的に言及しなくても、それが何であるかを人々に期待するのはかなり合理的である場合を示すために意図的に変更しました。とにかく、注目に値するのは、とは異なりReturnEmployeeNameReturnEmployeeIdsパラメーターの背後にある意味を示す名前に合理的に名前を変更できないことです。
イダンアリー

1
いずれにせよ、実際のプログラムでReturnEmployeeName(57)を記述することはほとんどありません。ReturnEmployeeName(employeeId)を記述する可能性ははるかに高くなります。従業員IDをハードコードするのはなぜですか?プログラマーのIDであり、何らかの詐欺が行われていない限り、この従業員の給与に少し追加します。:
ジェイ

0

はい、良いスタイルだと思います。

これは、ブール定数と整数定数にとって最も意味があります。

変数を渡す場合、変数名によって意味が明確になります。そうでない場合は、変数に適切な名前を付ける必要があります。

文字列を渡す場合、その意味はしばしば明確です。GetFooData( "Oregon")への呼び出しを見た場合のように、読者はパラメーターが状態名であるとほとんど推測できると思います。

もちろん、すべての文字列が明らかではありません。GetFooData( "M")が表示される場合、関数がわからない限り、 "M"の意味がわかりません。M = "管理レベルの従業員"のようなコードの場合は、おそらくリテラルではなく列挙型が必要であり、列挙型の名前は明確である必要があります。

そして、文字列は誤解を招く可能性があると思います。上記の私の例の「オレゴン」は、状態ではなく、製品やクライアントなどを指しているのかもしれません。

しかし、GetFooData(true)またはGetFooData(42)...それは何を意味する可能性があります。名前付きパラメーターは、自己文書化する素晴らしい方法です。何度もその目的のためにそれらを使用しました。


0

そもそもこのタイプの構文を使用する理由として、明確な理由はありません。

一般に、離れた場所で任意に見えるブール引数を持たないようにしてください。includeManagement(ほとんどの場合)結果に大きく影響します。しかし、この議論は「重みが少ない」ように見えます。

enumの使用について説明しましたが、引数が「重みを増やす」ように見えるだけでなく、メソッドのスケーラビリティも提供します。ただし、これはReturnEmployeeIds-methodがWhatToInclude-enumに合わせてスケーリングする必要があるため、すべての場合に最適なソリューションではない場合があります(NiklasJの回答を参照)。これは後で頭痛の種になるかもしれません。

これを考慮してください:WhatToInclude-enumをスケーリングし、ReturnEmployeeIds-methodをスケーリングしない場合。その後、スローArgumentOutOfRangeException(ベストケース)または完全に不要な(nullまたは空のList<Guid>)何かを返す場合があります。場合によってはReturnEmployeeIds、ソースコードがすぐに利用できないクラスライブラリにいる場合は特に、プログラマを混乱させる可能性があります。

訓練生がすべての「サブセット」であることがわかると、それWhatToInclude.Traineesがうまくいくと仮定しWhatToInclude.Allます。

これは(もちろん)実装されている方法 によって異なりReturnEmployeeIdsます。


ブール引数を渡すことができる場合、代わりに2つのメソッド(または必要に応じてそれ以上)に分割しようとします。一つは抽出かもしれないReturnAllEmployeeIdsReturnManagementIdsReturnRegularEmployeeIds。これはすべての基盤をカバーし、それを実装する人には絶対に自明です。これには、前述の「スケーリング」の問題もありません。

ブール引数を持つものの結果2つしかないためです。2つのメソッドを実装すると、余分な労力はほとんどかかりません。

コードが少ないほど、改善されることはめったにありません。


これは言って、そこにある明示的に引数を宣言し、読みやすさを向上させるいくつかのケースでは。例えば考えてみてください。GetLatestNews(max: 10)GetLatestNews(10)まだ一目瞭然です。の明示的な宣言は混乱maxを解消するのに役立ちます。

また、ブール引数が絶対に必要な場合は、trueまたはを読み取るだけではその使用法を推測できませんfalse。それから私は言うだろう:

var ids = ReturnEmployeeIds(includeManagement: true);

..は絶対的に優れており、より読みやすくなっています。

var ids = ReturnEmployeeIds(true);

2番目の例では、true何でも意味する可能性があるためです。しかし、私はこの議論を避けようとします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.