Un approfondimento sulla keyword “params”

Introduzione

In C#, attraverso la keyword params, è possibile specificare un numero variabile di argomenti per un metodo. Una possibilità sicuramente utile, quando vogliamo fornire una certa flessibilità a coloro che lo utilizzeranno. Ma come è gestito dal compilatore e dal CLR l’utilizzo di params ?

Il codice IL generato dal compilatore

Consideriamo il seguente codice, nel quale è definito un semplice metodo che esegue una somma di una serie di valori interi passati attraverso un array :

static int Add(params int[] array)
{
    int sum = 0;
    if (array != null)
    {
        for (int index = 0; index < array.Length; index++)
        {
            sum += array[index];
        }
    }
    return sum;
}

Ciò che osserviamo è l’utilizzo della keyowrd params prima della dichiarazione del parametro.

La tipica modalità con cui chiamiamo un metodo con questa firma è la seguente :

int[] array = new int[] { 1, 2, 3, 4, 5 };
int sum = Add(array);

oppure con una sola istruzione :

int sum = Add(new int[] { 1, 2, 3, 4, 5 });

Ciò che facciamo è definire un array di interi previsto dal metodo Add() e lo passiamo come parametro a quest’ultimo.

Ebbene, attraverso l’utilizzo di params, possiamo evitare di definire un array e passare direttamente l’elenco dei valori dei quali vogliamo eseguirne la somma.

int sum = Add(1, 2, 3, 4, 5);

Che cosa accade dietro le quinte che permette questo tipo di notazione ?

Banalmente, il compilatore genera del codice che alloca l’array e lo passa al metodo !

4503.params_60EE7B57

Nella figura precedente, ho evidenziato in rosso la parte di codice IL generata dal compilatore che non fa altro che creare un array con l’elenco dei valori da noi specificati ed invocare il metodo Add().

Nessun argomento ? La differenza di performance …

Detto ciò, consideriamo le due seguenti particolari invocazioni del metodo Add() :

Add();
Add(null);

Non è stato definita alcuna serie di valori sui quali eseguire la somma ma le due soluzioni sono nettamente diverse. Se la prima soluzione sembra essere stilisticamente la migliore, è la seconda che invece garantisce le migliori performance. Infatti, nel primo caso, il compilatore genera del codice IL che alloca un array di dimensione 0 mentre nel secondo caso ciò non accade.

2625.params2_6C7412C9

Possiamo rendercene conto anche eseguendo il debug e constatando che, nel primo caso, l’esecuzione supera il controllo array != null ma non riesce ad entrare nel ciclo for in quanto l’array ha dimensione 0.

6644.params3_7E703089

Nel secondo caso, il parametro array è null per cui il check suddetto non è superato.

1108.params4_077CBC0B

Conclusioni

In definitiva, l’uso di params non è la migliore delle soluzioni per le seguenti motivazioni :

  • viene sempre allocato un array sull’heap;
  • vengono eseguite le operazioni di inizializzazione dell’array;
  • essendo stata allocata memoria heap…c’è sempre il Garbage Collector in gioco;

In molti casi è sempre preferibile definire più overload dello stesso metodo con uno o più parametri e lasciare come ultima possibilità allo sviluppatore l’uso dell’overload con params.

Un esempio noto è il metodo String.Format della BCL che ha i seguenti overload :

  • string String.Format(string format, object arg0);
  • string String.Format(string format, object arg0, object arg1);
  • string String.Format(string format, object arg0, object arg1, object arg2);
  • string String.Format(string format, params object[] args);

In questo modo, lo sviluppatore utilizzerà sempre i primi tre overload fino ad un massimo di 3 parametri. Se necessita di passare un quarto parametro, verrà utilizzato l’ultimo overload con params.

L’individuazione del giusto overload da invocare viene eseguita ovviamente dal compilatore che, in primo luogo verifica se esiste un metodo che non ha params  e che riesce a matchare il numero di parametri passati. Se ciò non dovesse portare ad un risultato, allora il compilatore cerca il match con un metodo che ha una dichiarazione con params.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s