Mam następujący kod:
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;
}
Teraz dodałem komentarz do linii, że ReSharper sugeruje zmianę. Co to znaczy lub dlaczego trzeba go zmienić?implicitly captured closure: end, start
Odpowiedzi:
Ostrzeżenie mówi ci, że zmienne
end
istart
pozostają przy życiu, tak jak wszystkie lambdas w tej metodzie pozostają przy życiu.Spójrz na krótki przykład
Dostaję ostrzeżenie „Ukryte schwytanie: g” przy pierwszej lambdzie. Mówi mi, że
g
nie można zbierać śmieci, dopóki używana jest pierwsza lambda.Kompilator generuje klasę dla obu wyrażeń lambda i umieszcza wszystkie zmienne w tej klasie, które są używane w wyrażeniach lambda.
Więc w moim przykładzie
g
ii
są przetrzymywani w tej samej klasie w celu egzekucji moich delegatów. Jeślig
jest to ciężki obiekt z dużą ilością zasobów, śmiecinik nie może go odzyskać, ponieważ odwołanie w tej klasie jest wciąż aktywne, dopóki używane jest dowolne wyrażenie lambda. Jest to więc potencjalny wyciek pamięci i to jest przyczyną ostrzeżenia R #.@splintor Podobnie jak w języku C # anonimowe metody są zawsze przechowywane w jednej klasie na metodę, można tego uniknąć na dwa sposoby:
Użyj metody instancji zamiast anonimowej.
Podziel tworzenie wyrażeń lambda na dwie metody.
źródło
Random
instancji.Uzgodniony z Peterem Mortensenem.
Kompilator C # generuje tylko jeden typ, który zawiera wszystkie zmienne dla wszystkich wyrażeń lambda w metodzie.
Na przykład biorąc pod uwagę kod źródłowy:
Kompilator generuje typ wyglądający jak:
A
Capture
metoda jest kompilowany jako:Chociaż druga lambda nie używa
x
, nie można jej zbierać, ponieważx
jest ona kompilowana jako właściwość wygenerowanej klasy używanej w lambda.źródło
Ostrzeżenie jest ważne i wyświetlane w metodach, które mają więcej niż jedną lambda , i przechwytują różne wartości .
Po wywołaniu metody zawierającej lambdas tworzony jest instancja kompilowanego obiektu:
Jako przykład:
Sprawdź wygenerowany kod dla tej klasy (trochę uporządkowany):
Zwróć uwagę na wystąpienie
LambdaHelper
utworzonych sklepów zarównop1
ip2
.Wyobraź sobie, że:
callable1
zachowuje długotrwałe odniesienie do swojego argumentu,helper.Lambda1
callable2
nie przechowuje odniesienia do swojego argumentu,helper.Lambda2
W tej sytuacji odwołanie do
helper.Lambda1
pośrednio odwołuje się również do ciągup2
, a to oznacza, że śmieciarz nie będzie mógł go cofnąć. W najgorszym przypadku jest to wyciek pamięci / zasobów. Alternatywnie, może utrzymywać żywe obiekty dłużej niż jest to potrzebne, co może mieć wpływ na GC, jeśli zostaną awansowane z gen0 na gen1.źródło
p1
zcallable2
następującego:callable2(() => { p2.ToString(); });
- czy to nadal nie spowodowałoby tego samego problemu (śmieciarz nie będzie w stanie cofnąć przydziału), coLambdaHelper
nadal będzie zawieraćp1
ip2
?LambdaHelper
Powyżej) dla wszystkich lambd w metodzie nadrzędnej. Więc nawet jeślicallable2
nie zostanie wykorzystanyp1
, będzie współdzielić ten sam obiekt przechwytywania cocallable1
, a ten obiekt przechwytywania będzie odwoływał się zarówno do, jakp1
i dop2
. Zauważ, że to naprawdę ma znaczenie tylko dla typów referencyjnych,p1
aw tym przykładzie jest typem wartości.W przypadku zapytań Linq do Sql możesz otrzymać to ostrzeżenie. Zasięg lambda może przeżyć metodę, ponieważ zapytanie jest często aktualizowane po tym, jak metoda jest poza zakresem. W zależności od twojej sytuacji, możesz chcieć zaktualizować wyniki (tj. Poprzez .ToList ()) w ramach metody, aby umożliwić GC w zmiennych instancji metody zarejestrowanych w lambda L2S.
źródło
Zawsze możesz dowiedzieć się z powodów sugestii R #, po prostu klikając na wskazówki, jak pokazano poniżej:
Ta wskazówka skieruje cię tutaj .
źródło