Questa ispezione attira la vostra attenzione sul fatto che vengono acquisiti più valori di chiusura di quelli che sono ovviamente visibili, il che ha un impatto sulla durata di questi valori.
Considera il seguente codice:
using System;
public class Class1 {
private Action _someAction;
public void Method() {
var obj1 = new object();
var obj2 = new object();
_someAction += () => {
Console.WriteLine(obj1);
Console.WriteLine(obj2);
};
// "Implicitly captured closure: obj2"
_someAction += () => {
Console.WriteLine(obj1);
};
}
}
Nella prima chiusura, vediamo che sia obj1 che obj2 vengono catturati esplicitamente; possiamo vederlo solo guardando il codice. Per la seconda chiusura, possiamo vedere che obj1 viene catturato esplicitamente, ma ReSharper ci sta avvertendo che obj2 viene catturato implicitamente.
Ciò è dovuto a un dettaglio di implementazione nel compilatore C #. Durante la compilazione, le chiusure vengono riscritte in classi con campi che contengono i valori acquisiti e metodi che rappresentano la chiusura stessa. Il compilatore C # creerà solo una di queste classi private per metodo e se in un metodo è definita più di una chiusura, questa classe conterrà più metodi, uno per ogni chiusura e includerà anche tutti i valori acquisiti da tutte le chiusure.
Se osserviamo il codice generato dal compilatore, sembra un po 'così (alcuni nomi sono stati ripuliti per facilitare la lettura):
public class Class1 {
[CompilerGenerated]
private sealed class <>c__DisplayClass1_0
{
public object obj1;
public object obj2;
internal void <Method>b__0()
{
Console.WriteLine(obj1);
Console.WriteLine(obj2);
}
internal void <Method>b__1()
{
Console.WriteLine(obj1);
}
}
private Action _someAction;
public void Method()
{
// Create the display class - just one class for both closures
var dc = new Class1.<>c__DisplayClass1_0();
// Capture the closure values as fields on the display class
dc.obj1 = new object();
dc.obj2 = new object();
// Add the display class methods as closure values
_someAction += new Action(dc.<Method>b__0);
_someAction += new Action(dc.<Method>b__1);
}
}
Quando il metodo viene eseguito, crea la classe di visualizzazione, che acquisisce tutti i valori, per tutte le chiusure. Pertanto, anche se un valore non viene utilizzato in una delle chiusure, verrà comunque acquisito. Questa è la cattura "implicita" che ReSharper sta mettendo in evidenza.
L'implicazione di questa ispezione è che il valore di chiusura acquisito in modo implicito non verrà raccolto in modo inutile fino a quando non verrà raccolta la stessa immondizia. La durata di questo valore è ora legata alla durata di una chiusura che non utilizza esplicitamente il valore. Se la chiusura è di lunga durata, ciò potrebbe avere un effetto negativo sul codice, soprattutto se il valore acquisito è molto grande.
Si noti che sebbene si tratti di un dettaglio di implementazione del compilatore, è coerente tra versioni e implementazioni come Microsoft (pre e post Roslyn) o il compilatore di Mono. L'implementazione deve funzionare come descritto per gestire correttamente più chiusure acquisendo un tipo di valore. Ad esempio, se più chiusure acquisiscono un int, devono catturare la stessa istanza, il che può avvenire solo con una singola classe nidificata privata condivisa. L'effetto collaterale di ciò è che la durata di tutti i valori acquisiti è ora la durata massima di qualsiasi chiusura che acquisisce uno qualsiasi dei valori.