52

次の関数を検討してください。

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);

私は得る:Received an object

しかし、次のように呼び出す場合:

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

私は得る:Received an integer

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

4

6 に答える 6

55

varRHSによって型が決定されるようにするための単なる構文糖衣です。

あなたのコードで:

var a = (object)2;

次と同等です。

object a = (object)2;

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

についてdynamicは、Using Type dynamic を参照してください。型は静的型ですが、動的型のオブジェクトは静的型チェックをバイパスすることに注意してください。つまり、次の型を指定します。

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);

からIL_000fまでIL_007aですTestMethodTake(object)orTake(int)を直接呼び出すのではなく、次のようにメソッドを呼び出します。

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

の完全な IL TestClass:

.class public auto ansi abstract sealed beforefieldinit TestClass
    extends [mscorlib]System.Object
{
    // Nested Types
    .class nested private auto ansi abstract sealed beforefieldinit '<TestMethod>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<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>> '<>p__Site1'

    } // end of class <TestMethod>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<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>> TestClass/'<TestMethod>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.Type, class [mscorlib]System.Collections.Generic.IEnumerable`1<class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)
        IL_0054: call class [System.Core]System.Runtime.CompilerServices.CallSite`1<!0> class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)
        IL_0059: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>> TestClass/'<TestMethod>o__SiteContainer0'::'<>p__Site1'
        IL_005e: br.s IL_0060

        IL_0060: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>> TestClass/'<TestMethod>o__SiteContainer0'::'<>p__Site1'
        IL_0065: ldfld !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>>::Target
        IL_006a: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`3<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>> TestClass/'<TestMethod>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<class [System.Core]System.Runtime.CompilerServices.CallSite, class [mscorlib]System.Type, object>::Invoke(!0, !1, !2)
        IL_007f: nop
        IL_0080: ret
    } // end of method TestClass::TestMethod

} // end of class TestClass
于 2013-06-25T07:33:40.290 に答える
15

動的:

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

変数:

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

これにより、 の実行時に過負荷の解決が行われることがわかりますdynamic

したがって、変数bは次のように保持されますint

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は「オブジェクト」として保持されます

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

それが Take(a); の理由です。通話Take(object o)

static void Take(object o)
    {
        Console.WriteLine("Received an object");
    }
于 2013-06-25T07:17:29.273 に答える
1

動的変数の場合の型解決を理解するのに役立ちます。

  • 最初に行にブレークポイントを置きます:

    Take(b); //See here the type of b when u hover mouse over it, will be int

これは明らかに、コードdynamic b = (object)2が動的変数に割り当てられたときに 2 をオブジェクトに変換せず、b が int のままであることを意味します。

  • 次に、メソッドのオーバーロードをコメントアウトし、Take(int i)行にブレークポイントを配置しますTake(b)(同じ: type of bis still int)。ただし、実行すると、出力された値が表示されます: Recieved object.

  • ここで、dynamic変数呼び出しを以下のコードに変更します。

    Take((object)b); //It now prints "Received an object"

  • 次に、呼び出しを以下のコードに変更し、何が返されるかを確認します。

    dynamic b = (long)2;

    Take(b); // It now prints Received an object because there is no method overload that accepts a long and best matching overload is one that accepts an物体.

これは、実行時に保持する値に応じて動的変数に対して最適な型が解決され、動的変数に対して呼び出される最適なオーバーロード メソッドが実行時に解決されるためです。

于 2013-06-25T07:51:00.693 に答える
1

C# の仕様を見ると、次のようになります。

1.6.6.5 メソッドのオーバーロード

メソッドのオーバーロードでは、同じクラス内の複数のメソッドが一意のシグネチャを持っている限り、同じ名前を持つことができます。オーバーロードされたメソッドの呼び出しをコンパイルするとき、コンパイラはオーバーロードの解決を使用して、呼び出す特定のメソッドを決定します。

と:

7.5.4 動的オーバーロード解決のコンパイル時チェック

動的にバインドされたほとんどの操作では、解決の候補のセットはコンパイル時に不明です。ただし、場合によっては、候補セットはコンパイル時に認識されます。

  • 動的引数を使用した静的メソッド呼び出し

  • レシーバーが動的式ではないインスタンス メソッド呼び出し

  • レシーバーが動的式ではないインデクサー呼び出し

  • 動的引数を使用したコンストラクター呼び出し

これらの場合、各候補に対して制限付きのコンパイル時チェックが実行され、実行時にそれらのいずれかが適用される可能性があるかどうかが確認されます。

したがって、最初のケースでvarは動的ではなく、オーバーロードの解決により、コンパイル時にオーバーロード メソッドが検出されます。

しかし、2 番目のケースでは、動的引数を使用して静的メソッドを呼び出しています。オーバーロードの解決では、代わりに実行時にオーバーロード メソッドが検出されます。

于 2013-06-25T07:43:13.353 に答える
0

最初のケースでは var はobjectそうTake(object o)呼ばれることを意味します。2 番目のケースでは、dynamic型を使用し、ボックス化したにもかかわらず、その型intに関する情報がまだintあるため、最適な一致メソッドが呼び出されます。

于 2013-06-25T07:25:13.273 に答える