32

I have this code (the whole code is not important but can be seen on this link):

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

After opening the code in decompiler (ILSpy) for example I noticed the existence of newly created class <>c__DisplayClass0_0 by the C# compiler:

enter image description here

This wouldn't be a problem for me if this code wasn't critical for the performance of the system. This method is called millions of times and the garbage collector is cleaning these <>c__DisplayClass0_0 instances which slows down the performance:

enter image description here

How can I avoid creating this class (his instances and their garbage collecting) when using the Any method?

Why does the C# compiler create this class and is there any alternative of Any() I can use?

gnat
  • 6,213
  • 108
  • 53
  • 73
Nikolay Kostov
  • 16,433
  • 23
  • 85
  • 123
  • 4
    It needs to rewrite your code to find a safe home for the captured variables, otherPlayerCard and trumpCard here. Turning them from local variables into fields so their value can be preserved beyond the method body. DisplayClass is that safe home. – Hans Passant Sep 06 '15 at 16:40
  • 11
    Don't use LINQ on hot paths, that's the policy on the Roslyn codebase. – DaveShaw Sep 06 '15 at 16:40
  • 5
    I would typically avoid recommending micro-optimizations, but if this code is being run **millions** of times, refactoring this to optimize it for speed would be the solution here. LINQ is slow. – Nate Barbettini Sep 06 '15 at 16:41
  • 1
    @DaveShaw reference? – Daniel A. White Sep 06 '15 at 16:43
  • 3
    @DanielA.White - https://github.com/dotnet/roslyn/wiki/Contributing-Code - see Coding Conventions. – DaveShaw Sep 06 '15 at 17:28

2 Answers2

42

To understand the "display class" you have to understand closures. The lambda you pass here is a closure, a special type of method that magically drags in state from the scope of the method it's in and "closes around" it.

...except of course that there's no such thing as magic. All that state has to actually live somewhere real, somewhere that's associated with the closure method and readily available from it. And what do you call the programming pattern where you associate state directly with one or more methods?

That's right: classes. The compiler transforms the lambda into a closure class, then instantiates the class inside the hosting method so the hosting method can access the state in the class.

The only way to not have this happen is to not use closures. If this is really impacting performance, use an old-school FOR loop instead of a LINQ expression.

Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477
  • 12
    _"Any sufficiently advanced technology is indistinguishable from magic."_ - Arthur C. Clarke – Gusdor Sep 07 '15 at 07:03
25

How can I avoid creating this class (his instances and their garbage collecting) when using the Any method?

Why does the C# compiler creates this class and is there any alternative of Any() I can use?

Other posters already explained the why part, so the better question would be How can I avoid creation of a closure?. And the answer is simple: if lambda is using only the passed parameters and/or constants, the compiler will not create a closure. For instance:

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

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

The first will not create a closure while the second will.

With all that in mind, and assuming you don't want to use for/foreach loops, you can create own extension methods similar to those in System.Linq.Enumerable but with additional parameters. For this particular case, something like this would work:

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;
    }
} 

and change the code in question to:

var hasBigger =
    playerCards.Any(otherPlayerCard, 
        (c, opc) => c.Suit == opc.Suit
             && c.GetValue() > opc.GetValue());
Community
  • 1
  • 1
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • 1
    Hm, why would a closure not be created for parameters or instance members? I know of no way this could be pulled of. – usr Sep 07 '15 at 10:22
  • @usr it would create a static/instance function and bind a delegate to it. no need of a separate class because the whole state consist of arguments and/or `this`. – Ivan Stoev Sep 07 '15 at 11:53
  • OK, no need for a class. That's true. But the class has zero perf impact. Creating fresh delegate and closure instances is what's expensive. I believe the question is about perf. He is not concerned about the hidden class itself. – usr Sep 07 '15 at 11:57
  • @usr well, creating class instance per each call is definitely additional GC pressure. also, when lambda is not using instance members (like in the OP case), the compliler will generate a static field with the compiled delegate (once) – Ivan Stoev Sep 07 '15 at 12:02