0

I was reading https://blog.jetbrains.com/dotnet/2019/01/23/c-classes-memory-snapshots/ and refering to using "method group instead of a lambda" it correctly states:

We want to know if this approach will generate memory traffic. A quick look at the decompiled code says that yes, unfortunately, it will.

But, unfortunately, they do not explain, why.

I created my own example in https://sharplab.io/:

using System;
using System.Collections.Generic;
using System.Linq;

public class ClassWithAllocation {
    private string selector(string y){
        return y + this.GetHashCode(); //random example to demonstrate use of `this`
    }

    public void Method() {
        var source= new List<string>();
        source.Select(selector); //compiled to: Enumerable.Select(source, new Func<string, string>(selector));
    }
}

As noted in the comment, selector is compiled to create a new Func on every call.

When lambdas without closures are used, C# caches a Func instance that can be used for every call. (And: with closures this is not possible, because a different context must be "preserved" for every call.)

But why can't the above code compile to something like the following?

public class ClassWithoutAllocation {
    private readonly Func<string,string> _selector;
    
    public ClassWithoutAllocation(){
        _selector = new Func<string, string>(selector);
    }
    
    private string selector(string y){
        return y + this.GetHashCode(); //random example to demonstrate use of `this`
    }
    
    public void Method() {
        var source= new List<string>();
        source.Select(_selector); //compiled to: Enumerable.Select(source, _selector);
    }
}

SharpLab shows the code is basically compiled as is.

Follow up question: It being what it is, is the above pattern (storing a Func to use instead of a method group) a good pattern for avoiding heap allocations?

EDIT

This question was closed as a supposed duplicate of Why are there memory allocations when calling a func. But, as @JonSkeet says in his answer:

Unfortunately, the C# 5 specification currently requires that to create a new delegate instance each time it's run.

All of this makes me sad, and in the latest edition of the ECMA C# standard, the compiler will be permitted to cache the result of method group conversions. I don't know when/whether it will do so though.

So, I'm still wondering why would the compiler not cache a reference as in my example above (which is simpler than the one in the linked question)? What are the benefits of not doing so? What are the drawbacks of doing it?

mike
  • 1,627
  • 1
  • 14
  • 37
  • 1
    See also https://stackoverflow.com/questions/53208516/delegate-instance-allocation-with-method-group-compared-to and https://stackoverflow.com/a/49299523/34092 – mjwills Nov 30 '20 at 01:16
  • As to almost all questions that ask why a compiler or product behaves the way it does, without it being an apparent bug, the answer is almost always that someone decided that it should be this way. It may be something simple as they didn't prioritize doing it better or differently, or maybe never got the time to get back to it. Basically, you're asking for the meeting minutes from the meeting where they decided this. Rarely do we get the concrete answers to such questions though, so I wouldn't get my hopes up. – Lasse V. Karlsen Nov 30 '20 at 08:15

0 Answers0