5

I was surprised that there would be any runtime difference between these two delegates (fn1 and fn2):

static int SomeStaticMethod(int x) { return x; }

// fn1.Target == null in this case
Func<int, int> fn1 = SomeStaticMethod;

// fn2.Target != null in this case
Func<int, int> fn2 = x => x;

But apparently the second lambda is treated like an instance method, because its Target property is non-null. And it was treated differently before I switched to Visual Studio 2015 (in VS2012, I am pretty sure it was treated as a static method).

Is there a reason why a lambda with no closures is treated as a closed delegate (i.e. an instance method) in C#? I thought perhaps it's the debugger adding some stuff, but it also happens in release builds.

(Clarification)

The point is, I had a method like this which created a generic delegate for quickly converting from enums to ints (without boxing):

private static Func<int, TEnum> CreateIntToEnumDelegate()
{
    Func<int, int> lambda = x => x;
    return Delegate.CreateDelegate(typeof(Func<int, TEnum>), lambda.Method) 
        as Func<int, TEnum>;
}

and it didn't work in Roslyn anymore, because the "instance" lambda became incompatible with the delegate signature. So I had to use a static method instead, no big deal.

But what worries me is that this was impossible to catch during compile-time. This thread describes similar issues, btw, now that I searched for "Roslyn delegates".

vgru
  • 49,838
  • 16
  • 120
  • 201
  • 1
    Interesting, this is a second post regarding that for today - http://stackoverflow.com/questions/33948144/why-does-this-code-work-in-vs2013-and-does-not-work-in-2015 – Ivan Stoev Nov 27 '15 at 01:08
  • @Ivan: thanks for that link, yes, I thought this changed but didn't have VS2012 with me to check. I was pretty sure this worked differently in VS2012, so it seems like a breaking change in Visual Studio 2015, (although I cannot understand why, because I was building against .NET 4.5.2 in both VS2012 and now 2015, so I would presume it's the *framework* version that matters). – vgru Nov 27 '15 at 01:10
  • Looks like target framework doesn't matter - the change apparently originates from Roslyn. After all it's a compiler feature, not framework I guess. – Ivan Stoev Nov 27 '15 at 01:12
  • because the method is static. make it non static and you will see. `Func fn1 = new Program().Some_Non_StaticMethod;` – M.kazem Akhgary Nov 27 '15 at 01:12
  • @M.kazemAkhgary: yes, but I am arguing that `x => x` should also be treated as static. If nothing else, for performance reasons, because there is no need to use a "this-call" if there are no closures inside the body. – vgru Nov 27 '15 at 01:14

1 Answers1

4

This was a change made to Roslyn in 2014. It is quite strange, but it was actually done to improve performance. "In the new strategy all lambdas are emitted as instance methods" - from the discussion at roslyn.codeplex.com (note: dead link).

svick
  • 236,525
  • 50
  • 385
  • 514
Galax
  • 1,441
  • 7
  • 6
  • 1
    Thanks, this explains it. I wonder if there is a full list of these changes available anywhere. – vgru Nov 27 '15 at 09:58
  • As far as I can see this wasn't listed anywhere other than bug reports discussions on places like Codeplex and Github. I think they don't announce changes like this because it was behaviour that isn't defined either way by the spec. It should be defined though as your example shows that it can break working code. – Galax Nov 27 '15 at 17:26
  • 1
    I found [this document on GitHub](http://stackoverflow.com/a/33960229/69809) after some searching, it's probably not completely up to date, but seems to be the place where these things should get listed. – vgru Nov 30 '15 at 11:52