metodo c # con parametri illimitati o metodo con un array o un elenco?


21

Di recente ho appreso che è possibile creare un metodo con parametri illimitati, ad esempio:

SomeMethod(params int[] numbers);

ma la mia domanda è: qual è la differenza tra questo e la semplice creazione di un metodo che riceve un elenco o un array?

SomeMethod(int[] numbers);
SomeMethod(List<int> numbers);

forse ha qualche impatto sulle prestazioni? Non capisco perfettamente o vedo in che modo preferiresti quello con parametri illimitati.

Una rapida ricerca su Google non ha aiutato, spero che tu possa aiutarmi.



Oltre alla mia risposta di seguito, richiedeparams anche che il tipo di argomento sia un array. Se è necessario consumare una raccolta diversa da un array, potrebbe essere più logico fornire un non- paramsargomento.
digitlworld

1
@digitlworld: se questo argomento ti interessa, vedi github.com/dotnet/csharplang/issues/179 per la discussione.
Eric Lippert, il

Guarda la firma e / o l'implementazione di Console.Write.
3Daveva il

Risposte:


27

qual è la differenza tra questo e la semplice creazione di un metodo che riceve un elenco o un array?

La differenza tra

void M(params int[] x)

e

void N(int[] x)

è che M può essere chiamato così:

M(1, 2, 3)

o in questo modo:

M(new int[] { 1, 2, 3 });

ma N può essere chiamato solo nel secondo modo, non nel primo modo.

forse ha qualche impatto sulle prestazioni?

L'impatto sulle prestazioni è che, indipendentemente dal fatto che si chiami Mnel primo o nel secondo modo, viene creato un array. La creazione di un array ha un impatto sulle prestazioni perché richiede tempo e memoria. Ricordare che gli impatti sulle prestazioni devono essere misurati rispetto agli obiettivi di prestazione; è improbabile che il costo di creazione di un array aggiuntivo sia il fattore di gating che è la differenza tra successo e fallimento nel mercato.

Non capisco perfettamente o vedo in che modo preferiresti quello con parametri illimitati.

È puramente e interamente una comodità per l'autore del codice che chiama il metodo; è semplicemente più breve e più facile da scrivere

M(1, 2, 3);

invece di scrivere

M(new int[] { 1, 2, 3 });

Salva solo alcune sequenze di tasti dal lato del chiamante. Questo è tutto.

Alcune domande che non hai posto ma che vorresti conoscere la risposta a:

Come si chiama questa funzione?

I metodi che consentono il passaggio di un numero variabile di argomenti sul lato chiamante sono chiamati variadic . I metodi di Params sono il modo in cui C # implementa i metodi variadici.

Come funziona la risoluzione del sovraccarico con un metodo variadico?

Di fronte a un problema di risoluzione del sovraccarico, C # prenderà in considerazione sia la forma "normale" che "espansa" e la forma "normale" vince sempre se entrambe sono applicabili. Ad esempio, considera questo:

void P(params object[] x){}

e abbiamo una chiamata

P(null);

Vi sono due possibilità applicabili. In forma "normale", chiamiamo Pe passiamo un riferimento null per l'array. In forma "espansa", chiamiamo P(new object[] { null }). In questo caso, vince la forma normale. Se abbiamo ricevuto una chiamata, P(null, null)il modulo normale è inapplicabile e il modulo espanso vince per impostazione predefinita.

Sfida : supponiamo di avere var s = new[] { "hello" };e una chiamata P(s);. Descrivi cosa succede sul sito della chiamata e perché. Potresti essere sorpreso!

Sfida : supponiamo di avere entrambi void P(object x){}e void P(params object[] x){}. Cosa fa P(null)e perché?

Sfida : supponiamo di avere entrambi void M(string x){}e void M(params string[] x){}. Cosa fa M(null)e perché? In cosa differisce dal caso precedente?


Ch2: P(null)vs. P((object)null)vs. P((object[])null)- dove posso trovare una spiegazione per questa differenza? Ci si sente come se nullaveva qualche tipo speciale , che converte in serie piuttosto che oggetti ( P(null)), ma trovano la conversione di stringa o un array di stringhe ambiguo ( M(null)) ... che si sente molto strano, ci si aspetterebbe che fosse sia ambiguo in entrambi i casi o scegliere la versione single-arg (che non lo fa). Ma suppongo che si tratti più di paramsessere in qualche modo generico , come riferimento universale ( &&) nei modelli C ++, e quindi una migliore corrispondenza (in Ch2, non in Ch3).
firda,

@firda: puoi trovare la spiegazione nella specifica C #. Il motivo della stranezza è: null è convertibile in oggetto, stringa, oggetto [] e stringa []. Quindi la domanda quindi posta alla risoluzione di sovraccarico è: quale conversione è la migliore ? La regola di controllo in questo caso è specifica è migliore del generale . Come diciamo quale tipo è il più specifico? La regola è: tutte le giraffe sono animali ma non tutti gli animali sono giraffe, quindi la giraffa è più specifica dell'animale. Tutti lo object[]sono object, ma non tutti lo objectsono object[], quindi object[]è più specifico.
Eric Lippert,

@firda: ora sai perché le stringhe sono ambigue. NON è vero che "tutti lo string[]sono string". In effetti NO string[]sono stringe NO stringsono string[], quindi stringnon è più specifico o più generale di string[]e, quando viene chiesto di scegliere, viene visualizzato un errore di ambiguità.
Eric Lippert,

Tutti object[]sono object... object[]è più specifico, quindi P(null)vale per l'array più specifico, grazie, è quello che mi mancava. Trovate alcune regole qui: docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
firda

5

Ho appena fatto un piccolo prototipo. La risposta sembra essere paramssemplicemente lo zucchero sintattico per passare in un array. Non è davvero una sorpresa. Ho creato due versioni dello stesso metodo, in cui l'unica differenza è la parola chiave "params". L'IL generato per entrambi era identico, tranne per il fatto che a System.ParamArrayAttributeera stato applicato alla paramsversione.

Inoltre, l'IL generato nel sito di chiamata era lo stesso tra me chiamando il metodo con un dichiarato manualmente new int[]e chiamando il metodo semplicemente usando gli paramsargomenti.

Quindi, la risposta sembra essere "convenienza". Non sembra esserci alcuna differenza nelle prestazioni. Puoi anche chiamare aparams alternativa, funzione con un array, quindi non è altrettanto sorprendente. Resta da capire se è più facile per il consumatore il tuo metodo chiamarlo con un numero qualsiasi di parametri (ad es. someMethod(1, 2, 3)) Che dover sempre creare prima una raccolta (ad es someMethod(new List<int>() { 1, 2, 3 } ).).


4

La funzionalità di parametri illimitati offre i seguenti vantaggi in molti scenari:

  1. Accoppiamento lasco
  2. Riutilizzabilità migliorata
  3. Migliori prestazioni complessive dell'applicazione

Ecco un esempio in cui l'opzione dei parametri illimitati è un'ottima scelta

Considera che è necessario creare un'applicazione per l'invio di e-mail.

La funzione che invia l'e-mail deve essere in grado di gestire valori singoli o multipli per i campi 'A', 'CC' e 'BCC'.

Se i tipi di parametri sono fissi come matrici o elenchi per tutti i campi (To, CC, BCC), la funzione chiamante sarà costretta a gestire tutta la complessità della definizione di 3 matrici o elenchi per chiamare la funzione mittente e-mail .

Anche se il chiamante desidera inviare un'e-mail a un solo indirizzo, la funzione mittente e-mail forzerà il chiamante a definire e inviare 3 diversi array come parametri.

Se la funzione mittente e-mail utilizza l'approccio params illimitato, la funzione chiamante non deve gestire tutta la complessità.

L'approccio con parametri illimitati contribuisce a migliorare le prestazioni di runtime dell'applicazione evitando la creazione di array o elenchi laddove non necessario.


2

Dal punto di vista dello stile e non prestazionale , la paramsparola chiave è davvero bella da avere quando si desidera inviare un elenco opzionale di parametri.

Personalmente, userei paramsquando il mio codice era qualcosa del genere

SomeMethod('Option1', 'Option17');

void SomeMethod(params string[] options)
{
    foreach(var option in options)
    {
        switch(option): ...
    }
}

La parte bella di questo è che posso usare questo metodo ovunque senza dover creare un array o un elenco ogni volta.

Vorrei utilizzare arrayo listquando so che passerò sempre questa funzione un insieme di dati che sono già messi insieme

List<User> users = GetUsersFromDB();
SomeMethod(users);

Vedo il vantaggio della paramsflessibilità che aggiunge. Potrebbe avere un impatto relativamente minore sul tuo codice, ma è comunque uno strumento piacevole da avere.


1

La convenzione di chiamata è diversa. Per esempio ...

public class Test
{
    public void Method1( params int[] nums )
    {
        nums.ToList().ForEach( n => Console.WriteLine(n) );
    }

    public void Method2( List<int> nums )
    {
        nums.ForEach( n  => Console.WriteLine(n) );
    }   
}

void Main()
{   
    var t = new Test();
    t.Method1( 1, 2, 3, 4 );
    t.Method2( new List<int>() { 1, 2, 3, 4 } );
}

Nel primo caso, è possibile passare al metodo quanti ints quanti parametri separati. Nel secondo, è necessario creare un'istanza di un elenco e passarlo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.