C#でdynamicとvarをオブジェクトにキャストする

これらの機能を考えてみましょう。

static void Take(object o)
{
    Console.WriteLine("Received an object");
}

static void Take(int i)
{
    Console.WriteLine("Received an integer");
}

このように Take 関数を呼び出すと。

var a = (object)2;
Take(a);

オブジェクトを受け取りました。

しかし、このように呼び出すと

dynamic b = (object) 2;
Take(b);

というメッセージが表示されます。

両方のパラメータ (a & b) は object にキャストされます。しかし、なぜコンパイラはこのような動作をするのでしょうか?

ソリューション

var` は,RHS で型を決定させるための構文上の糖に過ぎない.

あなたのコードでは

var a = (object)2;

が相当します。

object a = (object)2;

オブジェクトに 2 をボックス化したので、オブジェクトを取得します。

dynamic` については、[Using Type dynamic]を参照するとよいでしょう。なお、型は静的型ですが、dynamic型のオブジェクトは静的型チェックをバイパスします、つまり、ofで指定した型です。

dynamic b = (object) 2;

はバイパスされ、実行時にその本当の型が解決されます。


実行時にどのように解決されるかについては、私はあなたが想像しているよりもずっと複雑だと思います。

例えば、次のようなコードがあったとします。

public static class TestClass {
    public static void Take(object o) {
        Console.WriteLine("Received an object");
    }

    public static void Take(int i) {
        Console.WriteLine("Received an integer");
    }

    public static void TestMethod() {
        var a=(object)2;
        Take(a);

        dynamic b=(object)2;
        Take(b);
    }
}

で、回答の後ろにフルIL(デバッグ設定の)を付けました。

この2行について

var a=(object)2;
Take(a);

はILのみです。

IL_0001: ldc.i4.2
IL_0002: box [mscorlib]System.Int32
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: call void TestClass::Take(object)

しかし、この2つについては

dynamic b=(object)2;
Take(b);

TestMethodIL_000f から IL_007a までです。これは Take(object)Take(int) を直接呼び出すのではなく、このようにメソッドを呼び出しています。

object b = 2;
if (TestClass.o__SiteContainer0.p__Site1 == null)
{
    TestClass.o__SiteContainer0.p__Site1 = CallSite.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "Take", null, typeof(TestClass), new CSharpArgumentInfo[]
    {
        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null),
        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
    }));
}
TestClass.o__SiteContainer0.p__Site1.Target(TestClass.o__SiteContainer0.p__Site1, typeof(TestClass), b);

TestClass` の完全な IL です。

.class public auto ansi abstract sealed beforefieldinit TestClass
    extends [mscorlib]System.Object
{
    // Nested Types
    .class nested private auto ansi abstract sealed beforefieldinit 'o__SiteContainer0'
        extends [mscorlib]System.Object
    {
        .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
            01 00 00 00
        )
        // Fields
        .field public static class [System.Core]System.Runtime.CompilerServices.CallSite`1 'p__Site1'

    } // end of class o__SiteContainer0

    // Methods
    .method public hidebysig static 
        void Take (
            object o
        ) cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 13 (0xd)
        .maxstack 8

        IL_0000: nop
        IL_0001: ldstr "Received an object"
        IL_0006: call void [mscorlib]System.Console::WriteLine(string)
        IL_000b: nop
        IL_000c: ret
    } // end of method TestClass::Take

    .method public hidebysig static 
        void Take (
            int32 i
        ) cil managed 
    {
        // Method begins at RVA 0x205e
        // Code size 13 (0xd)
        .maxstack 8

        IL_0000: nop
        IL_0001: ldstr "Received an integer"
        IL_0006: call void [mscorlib]System.Console::WriteLine(string)
        IL_000b: nop
        IL_000c: ret
    } // end of method TestClass::Take

    .method public hidebysig static 
        void TestMethod () cil managed 
    {
        // Method begins at RVA 0x206c
        // Code size 129 (0x81)
        .maxstack 8
        .locals init (
            [0] object a,
            [1] object b,
            [2] class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] CS$0$0000
        )

        IL_0000: nop
        IL_0001: ldc.i4.2
        IL_0002: box [mscorlib]System.Int32
        IL_0007: stloc.0
        IL_0008: ldloc.0
        IL_0009: call void TestClass::Take(object)
        IL_000e: nop
        IL_000f: ldc.i4.2
        IL_0010: box [mscorlib]System.Int32
        IL_0015: stloc.1
        IL_0016: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1 TestClass/'o__SiteContainer0'::'p__Site1'
        IL_001b: brtrue.s IL_0060

        IL_001d: ldc.i4 256
        IL_0022: ldstr "Take"
        IL_0027: ldnull
        IL_0028: ldtoken TestClass
        IL_002d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
        IL_0032: ldc.i4.2
        IL_0033: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
        IL_0038: stloc.2
        IL_0039: ldloc.2
        IL_003a: ldc.i4.0
        IL_003b: ldc.i4.s 33
        IL_003d: ldnull
        IL_003e: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)
        IL_0043: stelem.ref
        IL_0044: ldloc.2
        IL_0045: ldc.i4.1
        IL_0046: ldc.i4.0
        IL_0047: ldnull
        IL_0048: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)
        IL_004d: stelem.ref
        IL_004e: ldloc.2
        IL_004f: call class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, class [mscorlib]System.Collections.Generic.IEnumerable`1, class [mscorlib]System.Type, class [mscorlib]System.Collections.Generic.IEnumerable`1)
        IL_0054: call class [System.Core]System.Runtime.CompilerServices.CallSite`1 class [System.Core]System.Runtime.CompilerServices.CallSite`1::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)
        IL_0059: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1 TestClass/'o__SiteContainer0'::'p__Site1'
        IL_005e: br.s IL_0060

        IL_0060: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1 TestClass/'o__SiteContainer0'::'p__Site1'
        IL_0065: ldfld !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1::Target
        IL_006a: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1 TestClass/'o__SiteContainer0'::'p__Site1'
        IL_006f: ldtoken TestClass
        IL_0074: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
        IL_0079: ldloc.1
        IL_007a: callvirt instance void class [mscorlib]System.Action`3::Invoke(!0, !1, !2)
        IL_007f: nop
        IL_0080: ret
    } // end of method TestClass::TestMethod

} // end of class TestClass
解説 (3)

ダイナミックな

1.dynamicDynamically typed` です。 2.Dynamically typed - これは、宣言された変数の型が、実行時にコンパイラによって決定されることを意味します。

var:

1.varStatic typed` です。 2.静的型付け - これは、宣言された変数の型がコンパイル時にコンパイラによって決定されることを意味します。

これによって、dynamicでは実行時にOverloadの解決が行われることがわかります。

従って、変数 bint として保持されます。

dynamic b = (object) 2;
Take(b);

これが Take(b);Take(int i) を呼び出す理由です。

static void Take(int i)
    {
        Console.WriteLine("Received an integer");
    }

しかし、var a = (object)2 の場合、変数 a は 'object&#39 として保持されます。

var a = (object)2;
Take(a);

これが、Take(a); が Take(object o) を呼び出す理由です。

static void Take(object o)
    {
        Console.WriteLine("Received an object");
    }
解説 (15)

最初のケースでは、var はオブジェクトを意味するので、 Take(object o) が呼び出されます。2 番目のケースでは dynamic 型を使用しており、int をボックス化したにもかかわらず、int 型という情報を持っているので、最適なメソッドが呼び出されます。

解説 (0)