C#
.NET
MSIL
IL

C#で手軽にILや内部を確認するなら「SharpLab」!

SharpLabは、.NETのWeb Playgroundです。実行するだけでなく、ILやそのILをデコンパイルしたC#を確認することができます。C#だけじゃなくて、VB.NETやF#にも対応しています。

リンクはこちら : https://sharplab.io/

製作者さんはこちら : @ashmind https://twitter.com/ashmind

ソースコードはこちら : https://github.com/ashmind/SharpLab

C#の言語機能の内のいくつかはシンタックスシュガーです。SharpLabを使いILを確認することで、シンタックスシュガーの実現方法を確認することができます。ILだと読むのも一苦労ですね。SharpLabではILに加え、ILをデコンパイルしたC#を確認することができます。

また、「SharpLab」は


  • コード共有

  • Syntax Tree

  • HeapやStack

  • JIT Asm

などもできます。


C# 7.0から追加されたローカル関数を使ったコードは次の通りです。

このコード例は次のURLに!

https://sharplab.io/#v2:CYLg1APgAgDABFAjAbgLACgNQMwIEwKIDscA3hnJQrlACxwCyAhgJYB2AFAJRkVX/sALnADKARwCuTAE4BTDkLhsJAWx4BeAHxLVcAFQ6VaTOn4C2wwSxWy46uHmNmqigCrXZAZwUXDG7VY2+oZOznB8zkgAnBziUnIceFxcoZGIMe423tjJxgCQAL4YBUA=

using System;

public class Program {
public void Main() {
int Square(int num) => num * num;

int time = 2;
int Times(int num) => time * num;

Console.WriteLine(Square(2));
Console.WriteLine(Times(3));
}
}

このコードをSharpLabを使って、「IL」と「ILを経由してデコンパイルしたC#」を確認し、内部実装をみてみましょう。

これのILは次の通りです。

.class private auto ansi '<Module>'

{
} // end of class <Module>

.class public auto ansi beforefieldinit Program
extends [mscorlib]System.Object
{
// Nested Types
.class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass0_0'
extends [mscorlib]System.ValueType
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Fields
.field public int32 time

} // end of class <>c__DisplayClass0_0

// Methods
.method public hidebysig
instance void Main () cil managed
{
// Method begins at RVA 0x2050
// Code size 38 (0x26)
.maxstack 2
.locals init (
[0] valuetype Program/'<>c__DisplayClass0_0'
)

IL_0000: nop
IL_0001: nop
IL_0002: ldloca.s 0
IL_0004: ldc.i4.2
IL_0005: stfld int32 Program/'<>c__DisplayClass0_0'::time
IL_000a: nop
IL_000b: ldc.i4.2
IL_000c: call int32 Program::'<Main>g__Square|0_0'(int32)
IL_0011: call void [mscorlib]System.Console::WriteLine(int32)
IL_0016: nop
IL_0017: ldc.i4.3
IL_0018: ldloca.s 0
IL_001a: call int32 Program::'<Main>g__Times|0_1'(int32, valuetype Program/'<>c__DisplayClass0_0'&)
IL_001f: call void [mscorlib]System.Console::WriteLine(int32)
IL_0024: nop
IL_0025: ret
} // end of method Program::Main

.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2082
// Code size 8 (0x8)
.maxstack 8

IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Program::.ctor

.method assembly hidebysig static
int32 '<Main>g__Square|0_0' (
int32 num
) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x208b
// Code size 4 (0x4)
.maxstack 8

IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: mul
IL_0003: ret
} // end of method Program::'<Main>g__Square|0_0'

.method assembly hidebysig static
int32 '<Main>g__Times|0_1' (
int32 num,
valuetype Program/'<>c__DisplayClass0_0'& ''
) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x2090
// Code size 9 (0x9)
.maxstack 8

IL_0000: ldarg.1
IL_0001: ldfld int32 Program/'<>c__DisplayClass0_0'::time
IL_0006: ldarg.0
IL_0007: mul
IL_0008: ret
} // end of method Program::'<Main>g__Times|0_1'

} // end of class Program

ちょと頑張らないと読めませんね。これをデコンパイルしたC#はこちら。

using System;

using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
public class Program
{
[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <>c__DisplayClass0_0
{
public int time;
}

public void Main()
{
<>c__DisplayClass0_0 <>c__DisplayClass0_ = default(<>c__DisplayClass0_0);
<>c__DisplayClass0_.time = 2;
Console.WriteLine(<Main>g__Square|0_0(2));
Console.WriteLine(<Main>g__Times|0_1(3, ref <>c__DisplayClass0_));
}

[CompilerGenerated]
internal static int <Main>g__Square|0_0(int num)
{
return num * num;
}

[CompilerGenerated]
internal static int <Main>g__Times|0_1(int num, ref <>c__DisplayClass0_0 P_1)
{
return P_1.time * num;
}
}

デコンパイルしたC#をみると、ローカル関数の実現の仕方がよくわかりますね!


まとめ

「これILや中身どうやっているんだ?」と思ったら、とりあえずSharpLabを開きましょ!


関連・参考