paramsキーワードを使用する理由


336

これは基本的な質問ですが、回答が見つかりませんでした。

なぜそれを使うのですか?関数またはそれを使用するメソッドを作成した場合、それを削除しても、コードは完全に機能します。機能がない場合と同じように100%機能します。例えば:

パラメータあり:

static public int addTwoEach(params int[] args)
{
    int sum = 0;
    foreach (var item in args)
        sum += item + 2;
    return sum;
}

パラメータなし:

static public int addTwoEach(int[] args)
{
    int sum = 0;
    foreach (var item in args)
       sum += item + 2;
    return sum;
}

62
この方法自体のコードはまだ完璧に動作します... 呼び出すコード...よくないかもしれない
ジョンスキート

7
paramsキーワードは、メソッドに渡すことができる、または渡せないオプションのパラメーターを意味します。paramsキーワードのない配列は、メソッドに配列引数を渡す必要があることを意味します。
Ailayna Entarria 2014

Python言語は、ここで*述べたように、アスタリスク()が前に付いたパラメーターを使用して、同じ概念をとてもうまく実装しています
RBT 2018

回答:


485

paramsあなたはこのようなあなたのメソッドを呼び出すことができます。

addTwoEach(1, 2, 3, 4, 5);

なしparamsではできません。

さらに、どちらの場合でも、パラメータとして配列を使用てメソッドを呼び出すことができます。

addTwoEach(new int[] { 1, 2, 3, 4, 5 });

つまりparams、メソッドを呼び出すときにショートカットを使用できます。

無関係ですが、メソッドを大幅に短縮できます。

public static int addTwoEach(params int[] args)
{
    return args.Sum() + 2 * args.Length;
}

17
@ケン:System.Linq名前空間をインポートする必要があるかもしれません:)
Ranhiru Jude Cooray '28 / 09/28

49
またはargs.Sum(i => i + 2);を返します。
bornfromanegg 2015

2
ただし、デリゲートとの合計により、コンパイルされたコードの複雑さが増し、パフォーマンスが低下する可能性があります。クロージャーが発生しないため、この特定の状況にはあまり関係ありませんが、コンパイラーが実際に何をして最良の選択をするかを知っておく価値はあります。
アレンクラークコープランドJr

2
次も使用できます return args.Select(x => x + 2).Sum();
bbvg

1
追加するparamsと、呼び出し元やメソッドの解決を中断することなく、メソッド引数を追加できないようにロックされます。
ヤング氏

93

を使用paramsすると、引数なしで関数を呼び出すことができます。なしparams

static public int addTwoEach(int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

addtwoEach(); // throws an error

と比較params

static public int addTwoEach(params int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

addtwoEach(); // returns 0

一般に、引数の数が0から無限に変化する可能性がある場合はparamsを使用でき、引数の数が1から無限大に変化する場合は配列を使用できます。


13
実際には配列は空でもかまいません。new int[0]。お役に立てれば!:)
vidstige 2013

22

それはあなたが好きなだけあなたの呼び出しに多くの基本型パラメータを追加することを可能にします。

addTwoEach(10, 2, 4, 6)

一方、2番目の形式では、配列をパラメーターとして使用する必要があります

addTwoEach(new int[] {10,2,4,6})

17

一つの危険性paramsキーワードは、場合された後、メソッドの呼び出しは、符号化されました

  1. 誰かが誤って/意図的にメソッドシグネチャから1つ以上の必須パラメータを削除し、
  2. 署名の変更前のパラメーターの直前の1つ以上の必須パラメーターparamsは、paramsパラメーターとタイプ互換でした。

これらの呼び出しは、オプションのパラメーターとして扱われる必要なパラメーター用に以前は意図されていた1つ以上の式でコンパイルを続けますparams。私はこの最悪のケースに遭遇しました:paramsパラメータがタイプobject[]でした。

開発者は、必要なすべてのパラメーターを使用してパラメーターがメソッドから削除される、はるかに一般的なシナリオでコンパイラーが手首を叩くのに慣れているため、注目に値します(予期されるパラメーターの数が変化するため)。

私にとっては、ショートカットの価値はありません。 (Type)[]without paramsは、オーバーライドを必要とせずに0から無限のパラメーターで機能します。最悪の場合は、, new (Type) [] {}適用されない場所にをCalls に追加する必要があります。

ところで、imho、最も安全な(そして最も読みやすい習慣)とは:

  1. 名前付きパラメーターを介して渡す(VB; Pで実行可能になった後、C#で約20年でも実行できる)(理由:

    1.1。これは、パラメーターの順序付け、パラメーターの互換性タイプ、および/または呼び出しがコーディングされた後のカウント変更の後にパラメーターに渡される意図しない値の防止を保証する唯一の方法です。

    1.2。それが軽減新しい意味を反映した可能性の高い新しい識別子名は、右隣に渡される値になるため、パラメータの意味変化の後、それらのチャンスを、

    1.3。カンマを数え、呼び出しから署名に前後にジャンプして、どのパラメーターにどの式が渡されているかを確認する必要がなくなり、

    1.3.1。ちなみに、この理由だけはする必要がありますたくさん(ただしDRY原則を頻繁にエラーが発生しやすい違反を回避する観点から読みも言及しないようにコードを修正し、それを)が、この理由は、することができ、指数関数的に 1が存在する場合より重要/渡される式自体にコンマが含まれている(つまり、多次元配列参照またはマルチパラメーター関数呼び出し)。その場合、エディターの選択機能ですべてのオカレンスを検索して、コンマのカウントを自動化することはできます(可能な場合でも、メソッド呼び出しごとにパラメーターごとに追加のステップを追加します)。

    1.4。オプションパラメータを使用する必要がある場合(paramsまたはそうでない場合)、特定のオプションパラメータが渡された(したがって、そうではないか、少なくともデフォルト値ではない可能性がある)コールを検索できます。

(注:理由1.2。および1.3。は、最初の呼び出しをコーディングした場合でも、呼び出しを読み取ったり変更したりする必要がある場合は言うまでもなく、エラーの可能性を軽減および減らすことができます。)

そして

  1. そうするONE-PARAMETER-PER-LINEは読みやすくするためです(なぜなら:

    2.1。散らかりが少なく、

    2.2。右と左にスクロールする必要がありません(そしてPER-LINEを実行する必要があります。ほとんどの人間は複数行の左側を読み、右側にスクロールして右側を読むことができないためです)。

    2.3。渡されたすべてのパラメーターは本質的に割り当てステートメント(値またはローカル変数への参照の割り当て)であるため、これは割り当てステートメントのためにすでに発展した「ベストプラクティス」と一致しています。コーディングスタイルの最新の「ベストプラクティス」に従う人が1行に複数の割り当てステートメントをコーディングすることを夢見ないのと同じように、私たちはおそらくそうすべきではありません(「ベストプラクティス」私の「天才」に追いつくことはありません; P )パラメータを渡すときに行います。

  1. 名前がパラメータを反映する変数を渡しても、次の場合は役に立ちません。

    1.1。リテラル定数(つまり、「ベストプラクティス」でも名前付き定数を使用する必要がない単純な0/1、false / trueまたはnullを渡していて、その目的がメソッド名から簡単に推測できない場合) )、

    1.2。メソッドが大幅に低レベル/呼び出し元よりも汎用的であるため、変数にパラメーターと同じ/類似の名前を付けたくない(またはその逆)、または

    1.3。あなたのしている再整列/種類があるため、まだコンパイル前にコールをもたらす可能性が署名にパラメータを置き換える起こるまだ互換性があります。

  2. VSのような自動折り返し機能を使用しても、上記で説明した8つの理由の1つ(#2.2)しか削除されません。VS 2015までは、自動インデントが行われていなかったため(!?!本当に、MS?!?)、理由#2.1の重大度が高くなっています。

VSには、名前付きパラメーターを使用してメソッド呼び出しスニペットを生成するオプション(もちろん1行に1つずつ; P)と、名前付きパラメーターを必要とするコンパイラーオプション(概念的にはVBのOption Explicitに類似していますが、その要件は多分かつて考えられていました)同様にとんでもないですが、今では「「ベストプラクティス」」によって多用れています。実際、「私の中に戻っ日」;)、1991年に私のキャリアのわずか数か月前に、名前付きパラメーターを持つ言語を使用していた(または見たことがなかった)場合でも、私はアンチシープルを使用していました/「ただできる、という意味ではありません」 /「ローストの終わりを切る」という感覚を盲目的に「ローストの終わりをカットする」のではなく、インラインコメントを使用して、誰かがそうするのを見たことがないようにします。名前付きパラメーターを使用する必要はありませんソースコードのキーストローク)は、これらの構文のほとんどが始まっパンチカードの時代の遺物である。可読性が非常にある近代的なハードウェアとIDEのとはるかに複雑なソフトウェアとそのための言い訳はありません、多くの、MUCHより重要。「コードは書かれるよりもずっと頻繁に読まれる」。自動更新されていないコードを複製しない限り、保存されたすべてのキーストロークは、誰か(自分でも)が後でコードを読み取ろうとすると、飛躍的にコストがかかる可能性があります。


2
わかりません。少なくとも1つあることを強制できないのはなぜですか?パラメータがなくても、引数を渡しnullたりnew object[0]引数として渡したりするのを止めるものは何もありません。
ケーシー

オプションのパラメーターの前に必要なパラメーターを設定するのは、おそらく危険すぎます(呼び出しがコーディングされた後で、必須パラメーターの1つ以上が削除された場合)。オプションパラメータに関するドキュメントのサンプルコードでオプションパラメータの前に必要なパラメータを見たことがないのはそのためです。ところで、imho、最も安全な(そして最も読みやすい方法)は、名前付きパラメーターを介して渡すことです(これは、VBで実行可能になった後、C#で約20年後でも可能です)。VSには、名前付きパラメーターを使用してメソッド呼び出しスニペットを生成するオプションが必要です(行ごとに1つのパラメーターを作成します)。
Tom

どういう意味かよくわかりません。必須パラメーターとオプションのパラメーターを設定できる唯一の方法は、最初にすべての必須パラメーターを指定することです。
ケーシー

1
例 と宣言myMethodvoid myMethod(int requiredInt, params int[] optionalInts)ます。I /誰かのコード1 /より多くのコール、すなわちmyMethod(1)myMethod(1, 21)myMethod(1, 21, 22)。に変わりmyMethodますvoid myMethod(params int[] optionalInts)。これらの呼び出しはすべて、最初のパラメーター(「1」)が明らかにoptionalIntsパラメーターの最初の要素として渡されるように意図されていなくても、エラーなしでコンパイルされます。
トム

ああ。まあ、その特定のケースでは、それはお勧めできません。文字列と0対多の整数などが必要な場合、それを回避する理由はないと思います。
ケーシー

10

オーバーロードメソッドを作成する必要はありません。次に示すように、paramsを持つ単一のメソッドを使用するだけです。

// Call params method with one to four integer constant parameters.
//
int sum0 = addTwoEach();
int sum1 = addTwoEach(1);
int sum2 = addTwoEach(1, 2);
int sum3 = addTwoEach(3, 3, 3);
int sum4 = addTwoEach(2, 2, 2, 2);

入力をありがとうございます。ただしparams、コレクションの種類を渡してコレクションの数をカバーするだけであるため、これらの種類のオーバーロードは解決策になるとは思いません。
MasterMastic 2014

あなたはある程度正しいですが、クールなのは、入力パラメーターのないオーバーロードです。例:int sum1 = addTwoEach();
electricbah

5

params また、単一の引数でメソッドを呼び出すこともできます。

private static int Foo(params int[] args) {
    int retVal = 0;
    Array.ForEach(args, (i) => retVal += i);
    return retVal;
}

つまりのFoo(1);代わりにFoo(new int[] { 1 });。配列全体ではなく単一の値を渡す必要があるシナリオでの省略形に役立ちます。メソッドでも同じように処理されますが、この方法を呼び出すためのキャンディーがいくつかあります。


5

paramsキーワード自体を追加すると、そのメソッドを呼び出すときに複数のパラメーターを渡すことができますが、これを使用しないと不可能です。具体的には:

static public int addTwoEach(params int[] args)
{
    int sum = 0;

    foreach (var item in args)
    {
        sum += item + 2;
    }

    return sum;
}

上記のメソッドを呼び出すときは、次のいずれかの方法で呼び出すことができます。

  1. addTwoEach()
  2. addTwoEach(1)
  3. addTwoEach(new int[]{ 1, 2, 3, 4 })

しかし、paramsキーワードを削除する場合、上記の3番目の方法のみが正常に機能します。1番目と2番目のケースでは、エラーが発生します。


0

もう1つ重要な点を強調する必要があります。paramsパフォーマンスの点で優れているため、使用することをお勧めします。params引数を指定してメソッドを呼び出し、何も渡さない場合:

public void ExampleMethod(params string[] args)
{
// do some stuff
}

コール:

ExampleMethod();

次に、.Net Frameworkの新しいバージョンがこれを行います(.Net Framework 4.6から):

ExampleMethod(Array.Empty<string>());

このArray.Emptyオブジェクトは後でフレームワークで再利用できるため、冗長な割り当てを行う必要はありません。これらの割り当ては、このメソッドを次のように呼び出すと発生します。

 ExampleMethod(new string[] {});

0

愚かに聞こえるかもしれませんが、Paramsは多次元配列を許可しません。一方、多次元配列を関数に渡すことができます。


0

もう一つの例

public IEnumerable<string> Tokenize(params string[] words)
{
  ...
}

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