LoginSignup
28
17

More than 5 years have passed since last update.

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

Posted at

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に!

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を開きましょ!

関連・参考

28
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
17