I'm hoping someone can identify the language feature (or bug) that resulted in the change in behaviour of the program below. It is reproduced from a much larger scenario that was intended to log a message if the delegate supplied to Orchard::Go was not static.
using System;
namespace Sample
{
public static class Program
{
public static void Main()
{
new Apple();
}
}
public sealed class Apple
{
public Apple()
{
Orchard.Go(() => { });
}
}
internal static class Orchard
{
public static void Go(Action action)
{
Console.WriteLine(action.Method.IsStatic);
}
}
}
The scenario is:
I can see from ildasm the 2013 generated code...
___[MOD] C:\Sample.exe
| M A N I F E S T
|___[NSP] Sample
| |___[CLS] Sample.Apple
| | | .class public auto ansi sealed beforefieldinit
| | |___[STF] CS$9__CachedAnonymousMethodDelegate1 : private static class [mscorlib]System.Action
| | |___[MET] .ctor : void()
| | | b__0 : void()
| |
| |___[CLS] Sample.Orchard
| | | .class private abstract auto ansi sealed beforefieldinit
| | |___[STM] Go : void(class [mscorlib]System.Action)
| |
| |___[CLS] Sample.Program
| | | .class public abstract auto ansi sealed beforefieldinit
| | |___[STM] Main : void()
| |
|
...is clearly different to the 2015 generated code...
___[MOD] C:\Sample.exe
| M A N I F E S T
|___[NSP] Sample
| |___[CLS] Sample.Apple
| | | .class public auto ansi sealed beforefieldinit
| | |___[CLS] c
| | | | .class nested private auto ansi serializable sealed beforefieldinit
| | | | .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) ...
| | | |___[STF] 9 : public static initonly class Sample.Apple/'c'
| | | |___[STF] 9__0_0 : public static class [mscorlib]System.Action
| | | |___[STM] .cctor : void()
| | | |___[MET] .ctor : void()
| | | | b__0_0 : void()
| | |
| | |___[MET] .ctor : void()
| |
| |___[CLS] Sample.Orchard
| | | .class private abstract auto ansi sealed beforefieldinit
| | |___[STM] Go : void(class [mscorlib]System.Action)
| |
| |___[CLS] Sample.Program
| | | .class public abstract auto ansi sealed beforefieldinit
| | |___[STM] Main : void()
| |
|
...but my knowledge of IL and compiler changes isn't sufficient to determine whether this is a new feature or an unintended bug. I can produce the full IL dumps on request, but can anyone tell me from the information I've supplied what is going on here and whether it is intentional? Why is the anonymous method considered static in 2013 but non-static in 2015?
I logged this problem as a Microsoft Connect ticket here and was provided with a reference to the C# specification that confirmed you can't rely on any specific implementation for the enclosing type of an anonymous method. Specifically,
6.5.1/2 "The invocation list of a delegate produced from an anonymous function contains a single entry. The exact target object and target method of the delegate are unspecified. In particular, it is unspecified whether the target object of the delegate is null, the this value of the enclosing function member, or some other object."
The Microsoft Connect ticket also linked to a similar issue here if anyone is interested. So in my case specifically it appears the compiler is "Working as intended" and the solution to my problem is to "not do that".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With