Cette inspection attire votre attention sur le fait que plus de fermeture que ce qui est visible, ce qui a un impact sur la durée de vie de l'application. impact sur la durée de vie de ces valeurs.
Considérons le code suivant :
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);
};
}
}
Dans la première fermeture, nous voyons que les deux objets, obj1 et obj2, sont capturés explicitement ; nous pouvons le constater en regardant le code. Pour la deuxième fermeture, nous pouvons voir que obj1 est capturé explicitement, mais ReSharper nous avertit que obj2 est capturé implicitement.
Ceci est dû à un détail d'implémentation dans le compilateur C#. Lors de la compilation de compilation, les fermetures sont réécrites en classes avec des champs qui contiennent les valeurs capturées, et des méthodes qui représentent la fermeture elle-même. Le compilateur C# ne créera qu'une seule classe privée de ce type par méthode, et si plus d'une fermeture est définie dans une méthode, cette classe contiendra plusieurs méthodes, une pour chaque fermeture. contiendra plusieurs méthodes, une pour chaque fermeture, et elle inclura également elle inclura également toutes les valeurs capturées de toutes les fermetures.
Si on regarde le code que le compilateur génère, il a l'air un peu à ceci (certains noms ont été nettoyés pour faciliter la lecture) :
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);
}
}
Lorsque la méthode s'exécute, elle crée la classe d'affichage, qui capture toutes les valeurs, pour toutes les fermetures. Ainsi, même si une valeur n'est pas utilisée dans l'une des fermetures, elle sera quand même capturée. Il s'agit de la capture "implicite" que ReSharper met en évidence.
L'implication de cette inspection est que l'élément implicitement capturé capturée implicitement ne sera pas garbage collectée jusqu'à ce que la fermeture elle-même soit ramassée. La durée de vie de cette valeur est maintenant liée à la durée de vie d'une fermeture qui n'utilise pas explicitement la valeur. Si la fermeture a une longue durée de vie, cela peut avoir un effet négatif sur votre code, surtout si la valeur capturée est très grande.
Notez que, bien qu'il s'agisse d'un détail d'implémentation du compilateur, il est cohérent entre les versions et les implémentations telles que Microsoft (avant et après Roslyn) ou le compilateur de Mono. L'implémentation doit fonctionner comme décrit afin de gérer correctement les fermetures multiples qui capturent un type de valeur. Par exemple, si plusieurs fermetures capturent un int, alors elles doivent capturer la même instance, ce qui ne peut se produire qu'avec une classe imbriquée privée partagée unique. L'effet secondaire de ceci est que la durée de vie de toutes les valeurs capturées est maintenant la durée de vie maximale de toute fermeture qui capture une valeur. qui capture l'une de ces valeurs.
6 votes
MyCodeSucks s'il vous plaît, corrigez la réponse acceptée : celle de kevingessner est fausse (comme expliqué dans les commentaires) et l'avoir marquée comme acceptée va induire les utilisateurs en erreur s'ils ne remarquent pas la réponse de Console.
1 votes
Vous pouvez également rencontrer ce problème si vous définissez votre liste en dehors d'un try/catch et que vous effectuez tous vos ajouts dans le try/catch, puis définissez les résultats dans un autre objet. Le fait de déplacer la définition/l'ajout à l'intérieur du try/catch permettra la GC. J'espère que cela a du sens.