2015-12-14 10 views
5

Diciamo che abbiamo questa classe:Chiusure - Differenza tra catturare le variabili e la loro lettura come argomenti

// Provides deferred behaviour 
public class Command<TResult> 
{ 
    private Func<object[], TResult> _executeFunction; 
    private object[] _args; 

    public Command(Func<object[], TResult> execution, params object[] arguments) 
    { 
      _executeFunction = execution; 
      _args = arguments; 
    } 

    public TResult Execute() 
    { 
      return _executeFunction(_args); 
    } 
} 

Qual è la differenza tra queste due funzioni anonime?

int a = 1; 
int b = 4; 

// a and b are passed in as arguments to the function 
Command<int> sum = new Command<int>(args => (int)args[0] + (int)args[1], a, b); 

// a and b are captured by the function 
Command<int> sum2 = new Command<int>(_ => a + b); 

Console.WriteLine(sum.Execute()); //Prints out 5 
Console.WriteLine(sum2.Execute()); //Prints out 5 

Sono specificamente alla ricerca di differenze di prestazioni.

Inoltre, sappiamo che se una classe è in possesso di un riferimento di sum2, poi a e b vivranno al di là del campo di applicazione sono stati definiti in poi, probabilmente mai ottenere raccolti dal GC se la funzione è ancora riferimento da nessuna parte.

Succede lo stesso con sum? (Considerando gli argomenti sono tipi di riferimento e non valori come in questo esempio)

risposta

1

Quando si passano le variabili a e b, si sta facendo proprio questo. Il valore di 1 e 4 viene passato rispettivamente. Tuttavia, quando si fa riferimento a a e b all'interno del contesto (o dell'ambito) dell'espressione lambda, i valori vengono "acquisiti". Le variabili a e , nell'ambito di espressione lambda, vengono trattate come riferimenti agli originali al di fuori dell'ambito, il che significa che se vengono modificati nell'ambito di lambda, lo sono anche gli originali. Quando sono compilati in IL, risiedono in una classe in cui le istanze sono condivise.

static void Main() 
{ 
    int a = 1; 
    int b = 4; 

    // a and b are passed in as arguments to the function 
    var sum = new Command<int>(args => (int)args[0] + (int)args[1], a, b); 

    // a and b are captured by the function 
    var sum2 = new Command<int>(_ => 
    { 
     var c = a + b; 

     a++; 
     b++; 

     return c; 
    }); 

    Console.WriteLine(sum.Execute()); //Prints out 5 
    Console.WriteLine(sum2.Execute()); //Prints out 5 

    Console.WriteLine("a = " + a); // Prints 2 
    Console.WriteLine("b = " + b); // Prints 5 

    Console.ReadLine(); 
} 

C'è davvero piccola differenza nella IL, e io non credo che ci sia alcun implicazioni sulle prestazioni pena di evitare uno o l'altro. Preferirei di solito l'uso delle espressioni lambda per la loro leggibilità.

A look at some of the IL generated from some C# lambdas.

Problemi correlati