So I've been inspecting some 3rd party assembly with ILSpy & dnSpy and noticed various compiler generated classes and methods. Some examples:
Getter syntax like public Value { get; set; }
is changed into:
[CompilerGenerated]
public int GetValue()
{
return this.value;
}
[CompilerGenerated]
public void SetValue(int value)
{
this.value = value;
}
Event syntax like public event EventHandler SomeEvent;
is changed into:
[CompilerGenerated]
public void Add(EventHandler someHandler)
{
EventHandler eventHandler = this.eventHandler;
EventHandler eventHandler2;
do
{
eventHandler2 = eventHandler;
EventHandler value = (EventHandler)Delegate.Combine(eventHandler2, someHandler);
eventHandler = Interlocked.CompareExchange<EventHandler>(ref this.eventHandler, value, eventHandler2);
}
while (eventHandler != eventHandler2);
}
[CompilerGenerated]
public void Remove(EventHandler someHandler)
{
EventHandler eventHandler = this.eventHandler;
EventHandler eventHandler2;
do
{
eventHandler2 = eventHandler;
EventHandler value = (EventHandler)Delegate.Remove(eventHandler2, someHandler);
eventHandler = Interlocked.CompareExchange<EventHandler>(ref this.eventHandler, value, eventHandler2);
}
while (eventHandler != eventHandler2);
}
Also, yield return
leads to entire generated class that implements IEnumerator
interface, complex async
/await
flows are turned into state machine structs etc.
So the question is: how can I achieve the complier code generation behaviors in my assembly described above?
I've tried to reproduce these compiler code generations in my test assembly but no matter under which .NET framework version and C# language version I've built my test app (in Release mode with Optimize checkmark set) it produced nice and clean assembly where ILSpy is able to produce all my source code as it wasn't even compiled.
UPDATE: After turning off all ILSpy decompilation features I received somewhat similar results for my test assembly but there is still event
syntax present. So the follow-up question is: in which cases decompilers fail to automatically decompile various syntax structures of C# language and how to achieve such behavior in own assembly?
Some compiler features (you name some of them: Automatic properties or event handling) have no direct correspondence in the IL code and need to be translated to something a bit more complex than what you write in C#. Particularly "modern" coding features such as async/await are implemented mostly by the compiler, and not by the runtime. Therefore, such constructs generate quite complex code "under the hood" that the programmer normally doesn't see.
Whether a disassembler is able to recognize these structures and recreate the original, simple code is basically a question of the quality and age of the decompiler. Newer features are less likely to be recognized, and better (and probably non-free) decompilers such as dotPeek are more likely to get things right than simple, cheap decompilers. It's also not necessarily "better", to have everything correctly restored, since personally I often use the decompiler to check what my own code translates into.
Additionally, many public libraries (particularly those with source code on github) come included with a source reference. So when you open these assemblies in dotPeek, it will automatically download the original source, which not only contains the exact original code, but also all the comments. Great! Of course, this most of the time also applies to your own assemblies, since the debug information is usually available, too. So no real disassembling even takes place, it just shows the source.