J'ai le code suivant:
public double CalculateDailyProjectPullForceMax(DateTime date, string start = null, string end = null)
{
Log("Calculating Daily Pull Force Max...");
var pullForceList = start == null
? _pullForce.Where((t, i) => _date[i] == date).ToList() // implicitly captured closure: end, start
: _pullForce.Where(
(t, i) => _date[i] == date && DateTime.Compare(_time[i], DateTime.Parse(start)) > 0 &&
DateTime.Compare(_time[i], DateTime.Parse(end)) < 0).ToList();
_pullForceDailyMax = Math.Round(pullForceList.Max(), 2, MidpointRounding.AwayFromZero);
return _pullForceDailyMax;
}
Maintenant, j'ai ajouté un commentaire sur la ligne que ReSharper propose un changement. Qu'est-ce que cela signifie ou pourquoi devrait-il être modifié?implicitly captured closure: end, start
Réponses:
L'avertissement vous indique que les variables
end
etstart
restent en vie comme n'importe quel lambda à l'intérieur de cette méthode restent en vie.Jetez un oeil à l'exemple court
J'obtiens un avertissement "Fermeture implicitement capturée: g" au premier lambda. Cela me dit que
g
les déchets ne peuvent pas être ramassés tant que le premier lambda est utilisé.Le compilateur génère une classe pour les deux expressions lambda et place toutes les variables dans cette classe qui sont utilisées dans les expressions lambda.
Donc, dans mon exemple
g
eti
sont détenus dans la même classe pour l'exécution de mes délégués. S'ilg
s'agit d'un objet lourd avec beaucoup de ressources, le garbage collector n'a pas pu le récupérer, car la référence dans cette classe est toujours vivante tant que l'une des expressions lambda est utilisée. Il s'agit donc d'une fuite de mémoire potentielle, et c'est la raison de l'avertissement R #.@splintor Comme en C # les méthodes anonymes sont toujours stockées dans une classe par méthode, il y a deux façons d'éviter cela:
Utilisez une méthode d'instance au lieu d'une méthode anonyme.
Divisez la création des expressions lambda en deux méthodes.
la source
Random
instance.D'accord avec Peter Mortensen.
Le compilateur C # génère un seul type qui encapsule toutes les variables pour toutes les expressions lambda dans une méthode.
Par exemple, étant donné le code source:
Le compilateur génère un type qui ressemble à:
Et la
Capture
méthode est compilée comme:Bien que le second lambda n'utilise pas
x
, il ne peut pas être récupéré comme ilx
est compilé en tant que propriété de la classe générée utilisée dans le lambda.la source
L'avertissement est valide et affiché dans les méthodes qui ont plus d'un lambda , et elles capturent des valeurs différentes .
Lorsqu'une méthode qui contient des lambdas est invoquée, un objet généré par le compilateur est instancié avec:
Par exemple:
Examinez le code généré pour cette classe (rangé un peu):
Notez l'instance des
LambdaHelper
magasins créés à la foisp1
etp2
.Imagine ça:
callable1
garde une référence de longue date à son argument,helper.Lambda1
callable2
ne garde pas de référence à son argument,helper.Lambda2
Dans cette situation, la référence à fait
helper.Lambda1
également référence indirectement à la chaîne dansp2
, ce qui signifie que le garbage collector ne pourra pas la désallouer. Au pire, c'est une fuite de mémoire / ressource. Alternativement, il peut garder les objets en vie plus longtemps que nécessaire, ce qui peut avoir un impact sur GC s'ils sont promus de gen0 à gen1.la source
p1
de lacallable2
manière suivante:callable2(() => { p2.ToString(); });
- serait - ce pas encore faire la même question (garbage collector ne sera pas en mesure de le désallouer) commeLambdaHelper
va encore contenirp1
etp2
?LambdaHelper
-à- dire ci - dessus) pour tous les lambdas dans la méthode parent. Ainsi, même s'ilcallable2
n'était pas utilisép1
, il partagerait le même objet de capture quecallable1
, et cet objet de capture ferait référence à la fois àp1
etp2
. Notez que cela n'a vraiment d'importance que pour les types de référence, etp1
dans cet exemple, il s'agit d'un type de valeur.Pour les requêtes Linq à Sql, vous pouvez obtenir cet avertissement. La portée de lambda peut survivre à la méthode car la requête est souvent actualisée une fois que la méthode est hors de portée. Selon votre situation, vous souhaiterez peut-être actualiser les résultats (c'est-à-dire via .ToList ()) dans la méthode pour permettre le GC sur les variables d'instance de la méthode capturées dans le lambda L2S.
la source
Vous pouvez toujours comprendre avec une raison de suggestions R # simplement en cliquant sur les conseils comme indiqué ci-dessous:
Cet indice vous dirigera ici .
la source