2015-09-06 99 views
28

mam ten kod (cały kod nie jest ważna, ale widać na this link):Dlaczego kompilator C# tworzy prywatną DisplayClass przy użyciu metody LINQ Any() i jak mogę tego uniknąć?

internal static class PlayCardActionValidator 
{ 
    public static bool CanPlayCard(...) 
    { 
     // ... 
     var hasBigger = 
      playerCards.Any(
       c => c.Suit == otherPlayerCard.Suit 
        && c.GetValue() > otherPlayerCard.GetValue()); 
     // ... 
    } 
} 

Po otwarciu kod w Decompiler (ILSpy) na przykład zauważyłem istnienie nowo utworzonej klasie <>c__DisplayClass0_0 przez kompilator C#:

enter image description here

nie byłoby to dla mnie problemem, jeśli ten kod nie było krytyczne dla funkcjonowania systemu. Metoda ta nazywana jest miliony razy i garbage collector jest czyszczenie tych <>c__DisplayClass0_0 instancji, który spowalnia wydajność:

enter image description here

Jak mogę uniknąć tworzenia tej klasy (jego wystąpień i ich śmieci zbieranie) przy użyciu Any metoda?

Dlaczego kompilator C# tworzy tę klasę i czy istnieje alternatywa dla Any(), której mogę użyć?

+4

Trzeba przepisać kod znaleźć bezpieczny dom dla przechwyconych zmiennych, otherPlayerCard i trumpCard tutaj. Przekształcanie ich ze zmiennych lokalnych w pola, aby ich wartość mogła być zachowana poza treścią metody. DisplayClass to bezpieczny dom. –

+11

Nie używaj LINQ na gorących ścieżkach, to zasady dla kodu Roslyn. – DaveShaw

+5

Zazwyczaj unikałbym zalecania mikro-optymalizacji, ale jeśli ten kod jest uruchamiany ** miliony ** razy, rozwiązaniem byłoby tutaj refaktoryzacja w celu zoptymalizowania prędkości. LINQ jest powolny. –

Odpowiedz

36

Aby zrozumieć "klasę wyświetlania", należy rozumieć zamknięcia. Lambda, którą tu przechodzisz, to zamknięcie, specjalny rodzaj metody, która magicznie wciąga w stan z zakresu metody, w jakiej się znajduje i "zamyka się" wokół niej.

... z wyjątkiem oczywiście, że nie ma czegoś takiego jak magia. Cały ten stan musi faktycznie żyć gdzieś realnie, gdzieś, co jest związane z metodą zamknięcia i łatwo z niej korzystać. A jak nazywa się wzór programowania, w którym państwo bezpośrednio wiąże się z jedną lub większą liczbą metod?

To prawda: zajęć. Kompilator przekształca lambdę w klasę zamknięcia, a następnie tworzy instancję klasy w metodzie hostingu, aby metoda hostowania mogła uzyskać dostęp do stanu w klasie.

Jedynym sposobem, aby tego nie zrobić, jest nieużywanie zamknięć. Jeśli rzeczywiście ma to wpływ na wydajność, użyj pętli old-school FOR zamiast wyrażenia LINQ.

+11

_ "Każda wystarczająco zaawansowana technologia jest nieodróżnialna od magii." - Arthur C. Clarke – Gusdor

23

Jak mogę uniknąć tworzenia tej klasy (jego instancji i gromadzenia śmieci) podczas korzystania z metody Any?

Dlaczego kompilator C# tworzy tę klasę i czy istnieje alternatywa Any(), której mogę użyć?

Inne plakaty już wyjaśnił dlaczego udział, więc lepiej byłoby pytanie Jak mogę uniknąć tworzenia zamknięcia?. Odpowiedź jest prosta: jeśli lambda używa tylko przekazanych parametrów i/lub stałych, kompilator nie utworzy zamknięcia. Na przykład:

bool AnyClub() { return playerCards.Any(c => c.Suit == CardSuit.Club); } 

bool AnyOf(CardSuit suit) { return playerCards.Any(c => c.Suit == suit); } 

Pierwsza nie stworzy zamknięcia, a druga nie.

Z wszystkich to na uwadze, a zakładając, że nie chcą korzystać do/foreach pętle, można tworzyć własne rozszerzenia metody podobne do tych w System.Linq.Enumerable ale z dodatkowymi parametrami. W tym konkretnym przypadku, coś jak to będzie działać:

public static class Extensions 
{ 
    public static bool Any<T, TArg>(this IEnumerable<T> source, TArg arg, Func<T, TArg, bool> predicate) 
    { 
     foreach (var item in source) 
      if (predicate(item, arg)) return true; 
     return false; 
    } 
} 

i zmienić kod w pytaniu do:

var hasBigger = 
    playerCards.Any(otherPlayerCard, 
     (c, opc) => c.Suit == opc.Suit 
      && c.GetValue() > opc.GetValue()); 
+1

Hm, dlaczego nie można utworzyć zamknięcia dla parametrów lub członków instancji? Nie wiem, jak można go wyciągnąć. – usr

+0

@ us tworzyłby funkcję statyczną/instancyjną i wiązała do niej delegata. nie potrzeba oddzielnej klasy, ponieważ całe państwo składa się z argumentów i/lub "tego". –

+0

OK, nie ma potrzeby zajęcia. To prawda. Ale klasa ma zerowy wpływ perfekcji. Tworzenie nowych delegatów i zamknięć jest drogie. Uważam, że pytanie dotyczy perf. Nie przejmuje się ukrytą klasą. – usr