Warum das Schlüsselwort params verwenden?


336

Ich weiß, dass dies eine grundlegende Frage ist, aber ich konnte keine Antwort finden.

Warum es benutzen? Wenn Sie eine Funktion oder eine Methode schreiben, die sie verwendet, funktioniert der Code beim Entfernen immer noch einwandfrei, zu 100% wie ohne. Z.B:

Mit Params:

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

Ohne Parameter:

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

62
Der Code der Methode selbst wird immer noch perfekt funktionieren ... der aufrufende Code kann durchaus nicht ...
Jon Skeet

7
Das Schlüsselwort params bedeutet OPTIONALE Parameter, die an die Methode übergeben werden können oder nicht. Ein Array ohne das Schlüsselwort params bedeutet, dass Sie das Array-Argument an die Methode übergeben MÜSSEN.
Ailayna Entarria

Die Python-Sprache implementiert das gleiche Konzept so süß mit einem *Parameter mit dem Präfix asterisk ( ), wie hier erwähnt .
RBT

Antworten:


485

Mit könnenparams Sie Ihre Methode folgendermaßen aufrufen:

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

Ohne paramskannst du nicht.

Darüber hinaus können Sie die Methode in beiden Fällen mit einem Array als Parameter aufrufen :

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

Das heißt, paramsSie können beim Aufrufen der Methode eine Verknüpfung verwenden.

Unabhängig davon können Sie Ihre Methode drastisch verkürzen:

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

17
@ Ken: Möglicherweise müssen Sie den System.LinqNamespace importieren :)
Ranhiru Jude Cooray

49
Oder return args.Sum (i => i + 2);
geboren

2
Die Summe mit einem Delegaten erhöht jedoch die Komplexität des kompilierten Codes, was möglicherweise weniger leistungsfähig sein kann. In dieser speziellen Situation nicht wirklich relevant, da dies zu keinen Schließungen führen würde, aber es lohnt sich zu wissen, was der Compiler tatsächlich tut, um die beste Wahl zu treffen.
Allen Clark Copeland Jr.

2
Sie könnten auch verwenden return args.Select(x => x + 2).Sum();
bbvg

1
Wenn Sie fügen paramsSie sperren sich aus das Hinzufügen zusätzlicher Methodenargumente ohne Ihre Anrufer oder Methode Auflösung zu brechen.
Mr. Young

93

Mit paramskönnen Sie die Funktion ohne Argumente aufrufen. Ohne params:

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

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

    return sum;
}

addtwoEach(); // throws an error

Vergleiche mit params:

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

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

    return sum;
}

addtwoEach(); // returns 0

Im Allgemeinen können Sie Parameter verwenden, wenn die Anzahl der Argumente von 0 bis unendlich variieren kann, und ein Array verwenden, wenn die Anzahl der Argumente von 1 bis unendlich variiert.


13
Tatsächlich kann ein Array leer sein. new int[0]. hoffe das hilft! :)
vidstige

22

Sie können Ihrem Aufruf beliebig viele Basistypparameter hinzufügen.

addTwoEach(10, 2, 4, 6)

Bei der zweiten Form müssen Sie ein Array als Parameter verwenden

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

17

Eine Gefahr bei paramsSchlüsselwörtern besteht darin, dass nach dem Codieren von Aufrufen zur Methode

  1. jemand entfernt versehentlich / absichtlich einen / mehrere erforderliche Parameter aus der Methodensignatur und
  2. Ein / mehrere erforderliche Parameter unmittelbar vor dem paramsParameter vor der Signaturänderung waren typkompatibel mit dem paramsParameter.

Diese Aufrufe werden weiterhin mit einem oder mehreren Ausdrücken kompiliert, die zuvor für erforderliche Parameter vorgesehen waren und als optionaler paramsParameter behandelt werden. Ich bin gerade auf den schlimmsten Fall paramsgestoßen : Der Parameter war vom Typ object[].

Dies ist insofern bemerkenswert, als Entwickler es gewohnt sind, dass der Compiler ihre Handgelenke mit dem viel häufigeren Szenario schlägt, in dem Parameter aus einer Methode mit allen erforderlichen Parametern entfernt werden (da sich die Anzahl der erwarteten Parameter ändern würde).

Für mich ist es die Abkürzung nicht wert. (Type)[]without paramsfunktioniert mit 0 bis unendlich vielen Parametern, ohne dass Overrides erforderlich sind. Im schlimmsten Fall müssen Sie einen Anruf hinzufügen , new (Type) [] {}, wenn dies nicht zutrifft.

Übrigens ist imho die sicherste (und am besten lesbare) Praxis:

  1. über benannte Parameter übergeben (was wir jetzt sogar in C # ~ 2 Jahrzehnten tun können, nachdem wir in VB; P konnten) (weil:

    1.1. Dies ist die einzige Möglichkeit, um zu verhindern , dass unbeabsichtigte Werte nach Parameterreihenfolge, Änderung des Kompatibilitätstyps und / oder Anzahl nach der Codierung von Anrufen an Parameter übergeben werden.

    1.2. Es verringert diese Chancen nach einer Änderung der Parameterbedeutung, da der wahrscheinliche neue Bezeichnername, der die neue Bedeutung widerspiegelt, direkt neben dem an ihn übergebenen Wert steht.

    1.3. Es wird vermieden, Kommas zu zählen und vom Aufruf zur Signatur hin und her zu springen, um zu sehen, welcher Ausdruck für welchen Parameter übergeben wird, und

    1.3.1. Übrigens sollte dieser Grund allein ausreichend sein (um häufige fehleranfällige Verstöße gegen das DRY-Prinzip zu vermeiden, nur um den Code zu lesen und nicht zu ändern ), aber dieser Grund kann exponentiell wichtiger sein, wenn es einen gibt / Es werden mehr Ausdrücke übergeben, die selbst Kommas enthalten, z. B. mehrdimensionale Array-Refs oder Funktionsaufrufe mit mehreren Parametern. In diesem Fall konnten Sie nicht einmal eine Funktion "Alle Vorkommen in einer Auswahl suchen" in Ihrem Editor verwenden, um die Kommazählung für Sie zu automatisieren (was selbst dann noch einen zusätzlichen Schritt pro Parameter und Methodenaufruf hinzufügen würde ).

    1.4. Wenn Sie optionale Parameter verwenden müssen ( paramsoder nicht), können Sie nach Anrufen suchen, bei denen ein bestimmter optionaler Parameter übergeben wird (und daher höchstwahrscheinlich nicht oder zumindest möglicherweise nicht der Standardwert ist).

(HINWEIS: Die Gründe 1.2 und 1.3 können die Fehlerwahrscheinlichkeit verringern und verringern, selbst wenn die ersten Anrufe codiert werden, ganz zu schweigen davon, wann Anrufe gelesen und / oder geändert werden müssen.))

und

  1. Tun Sie dies ONE - PARAMETER - PER - LINE für eine bessere Lesbarkeit (weil:

    2.1. es ist weniger überladen, und

    2.2. Es wird vermieden, nach rechts und hinten nach links scrollen zu müssen (und dies PERLINE, da die meisten Sterblichen den linken Teil mehrerer Zeilen nicht lesen können, nach rechts scrollen und den rechten Teil lesen können).

    2.3. Dies steht im Einklang mit der "Best Practice", die wir bereits für Zuweisungsanweisungen entwickelt haben, da jeder übergebene Parameter im Wesentlichen eine Zuweisungsanweisung ist (Zuweisen eines Werts oder einer Referenz zu einer lokalen Variablen). Genau wie diejenigen, die folgen neuesten „Best Practice“ in Code Stil würde nicht der Codierung mehrerer Zuweisungsanweisungen pro Zeile träumen, wir wahrscheinlich nicht (und wird nicht einmal „Best Practice“ aufholt zu meinem „Genie“; P ) tun Sie dies, wenn Sie Parameter übergeben.

ANMERKUNGEN :

  1. Das Übergeben von Variablen, deren Namen die Parameter widerspiegeln, hilft nicht, wenn:

    1.1. Sie übergeben Literalkonstanten (dh eine einfache 0/1, false / true oder null, für die selbst bei "Best Practices" möglicherweise keine benannte Konstante verwendet werden muss und deren Zweck nicht einfach aus dem Methodennamen abgeleitet werden kann ),

    1.2. Die Methode ist wesentlich niedriger / allgemeiner als der Aufrufer, sodass Sie Ihre Variablen nicht gleich / ähnlich wie die Parameter benennen möchten / können (oder umgekehrt) oder

    1.3. Sie ordnen Parameter in der Signatur neu an / ersetzen sie, was dazu führen kann, dass frühere Anrufe noch kompiliert werden, da die Typen zufällig noch kompatibel sind.

  2. Mit einer Auto-Wrap-Funktion wie VS werden nur EINER (# 2.2) der 8 oben genannten Gründe beseitigt. Noch vor VS 2015 wurde NICHT automatisch eingerückt (!?! Wirklich, MS?!?), Was die Schwere des Grundes # 2.1 erhöht.

VS sollte eine Option haben, die Methodenaufruf-Snippets mit benannten Parametern generiert (einer pro Zeile natürlich; P), und eine Compiler-Option, die benannte Parameter erfordert (ähnlich im Konzept wie Option Explicit in VB, an die übrigens die Anforderung von früher häufig gedacht wurde ebenso empörend, wird aber jetzt von "Best Practices" häufig gefordert . In der Tat "zurück in meinemTag ";), 1991, nur wenige Monate nach meiner Karriere, noch bevor ich eine Sprache mit benannten Parametern benutzte (oder sogar gesehen hatte), hatte ich das Anti-Schaf /" Nur weil du kannst, heißt nicht, dass du es solltest " / "Schneiden Sie die Enden des Bratens nicht blind genug", um ihn zu simulieren (mithilfe von Inline-Kommentaren), ohne dass jemand dies gesehen hat. Sie müssen keine benannten Parameter verwenden (sowie keine andere Syntax, die "'kostbar' 'spart). Quellcode Anschläge) ist ein Relikt aus der Ära Lochkarte , wenn die meisten dieser Syntaxen gestartet. Es gibt keine Entschuldigung dafür mit moderner Hardware und IDE und viel komplexer Software , wo Lesbarkeit viel, viel, vIELwichtiger. "Code wird viel häufiger gelesen als geschrieben". Solange Sie keinen nicht automatisch aktualisierten Code duplizieren, kostet jeder gespeicherte Tastendruck wahrscheinlich exponentiell mehr, wenn jemand (auch Sie selbst) versucht, ihn später zu lesen.


2
Ich verstehe nicht Warum können Sie nicht einfach durchsetzen, dass es mindestens eine gibt? Auch ohne Parameter hindert Sie nichts daran, zu bestehen nulloder new object[0]als Argument.
Casey

Es ist wahrscheinlich einfach zu gefährlich, jemals erforderliche Parameter vor dem optionalen zu haben (falls einer oder mehrere dieser erforderlichen Parameter entfernt werden, nachdem Anrufe codiert wurden). Das kann der Grund sein, warum ich in keinem Dokument zu optionalen Parametern die erforderlichen Parameter vor dem optionalen Parameter im Beispielcode gesehen habe. Übrigens ist es imho die sicherste (und am besten lesbare) Praxis, benannte Parameter zu übergeben (was wir jetzt sogar in C # ~ 2 Jahrzehnten tun können, nachdem wir es in VB konnten). VS sollte über eine Option verfügen, die Methodenaufruf-Snippets mit benannten Parametern generiert (und zwar 1 Parameter pro Zeile).
Tom

Ich bin mir nicht sicher, was du meinst. Die einzige Möglichkeit, erforderliche und optionale Parameter zu haben, besteht darin, zuerst alle erforderlichen Parameter anzugeben.
Casey

1
Ex. Ich erkläre myMethodals void myMethod(int requiredInt, params int[] optionalInts). I / jemand anderes Codes ein / mehrere Anrufe, das heißt myMethod(1), myMethod(1, 21), myMethod(1, 21, 22). Ich ändere mich myMethodzu sein void myMethod(params int[] optionalInts). Alle diese Aufrufe werden weiterhin fehlerfrei kompiliert, obwohl ihre ersten Parameter (die "1") eindeutig nicht als erstes Element des optionalIntsParameters übergeben werden sollten.
Tom

Oh. Nun, OK, in diesem speziellen Fall kann es schlecht beraten sein. Ich glaube nicht, dass es einen Grund gibt, dies zu vermeiden, wenn Sie eine Zeichenfolge und 0 zu viele Ints oder was auch immer benötigen.
Casey

10

Es müssen keine Überladungsmethoden erstellt werden. Verwenden Sie einfach eine einzelne Methode mit den unten gezeigten Parametern

// 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);

Vielen Dank für Ihre Eingabe, aber ich denke nicht, dass diese Art von Überladungen überhaupt eine Lösung darstellen würde, da paramswir mit oder ohne nur einen Sammlungstyp übergeben würden, um die Anzahl der Sammlungen abzudecken.
MasterMastic

Sie haben in gewissem Maße Recht, aber was es cool macht, ist eine Überlastung ohne Eingabeparameter, z. B. int sum1 = addTwoEach ();
Electricbah

5

params Außerdem können Sie die Methode mit einem einzigen Argument aufrufen.

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

dh Foo(1);statt Foo(new int[] { 1 });. Kann für Abkürzungen in Szenarien nützlich sein, in denen Sie möglicherweise einen einzelnen Wert anstelle eines gesamten Arrays übergeben müssen. Es wird in der Methode immer noch genauso gehandhabt, aber es gibt Süßigkeiten für den Aufruf auf diese Weise.


5

Das Hinzufügen des Schlüsselworts params selbst zeigt, dass Sie beim Aufrufen dieser Methode mehrere Parameter übergeben können, was ohne die Verwendung nicht möglich ist. Um genauer zu sein:

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

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

    return sum;
}

Wenn Sie die obige Methode aufrufen, können Sie sie auf eine der folgenden Arten aufrufen:

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

Wenn Sie jedoch das Schlüsselwort params entfernen, funktioniert nur der dritte der oben angegebenen Wege einwandfrei. Für den 1. und 2. Fall erhalten Sie eine Fehlermeldung.


0

Eine weitere wichtige Sache muss hervorgehoben werden. Es ist besser zu verwenden, paramsweil es besser für die Leistung ist. Wenn Sie eine Methode mit paramsArgument aufrufen und nichts übergeben:

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

Anruf:

ExampleMethod();

Dann tun dies neue Versionen des .Net Frameworks (ab .Net Framework 4.6):

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

Dieses Array.EmptyObjekt kann später vom Framework wiederverwendet werden, sodass keine redundanten Zuweisungen erforderlich sind. Diese Zuordnungen treten auf, wenn Sie diese Methode wie folgt aufrufen:

 ExampleMethod(new string[] {});

0

Klingt vielleicht dumm, aber Params erlaubt kein mehrdimensionales Array. Während Sie ein mehrdimensionales Array an eine Funktion übergeben können.


0

Ein anderes Beispiel

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

var items = Tokenize(product.Name, product.FullName, product.Xyz)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.