it-swarm.com.de

Umwandeln von Dynamic und Var in Object in C #

Betrachten Sie diese Funktionen:

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

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

Wenn ich die Funktion Take so aufrufe:

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

Ich bekomme :Received an object

Aber wenn es so heißt:

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

Ich bekomme:Received an integer

Beide Parameter (a & b) werden in object umgewandelt. Aber warum hat der Compiler dieses Verhalten?

52
user1968030

var ist nur ein syntaktischer Zucker, mit dem der Typ von RHS bestimmt werden kann.

In Ihrem Code:

var a = (object)2;

ist äquivalent zu:

object a = (object)2;

Sie erhalten ein Objekt, da Sie ein Objekt mit 2 Markiert haben.

Für dynamic möchten Sie vielleicht einen Blick auf sing Type dynamic werfen. Beachten Sie, dass der Typ ein statischer Typ ist, ein Objekt vom Typ dynamic jedoch die statische Typprüfung umgeht , dh den von Ihnen angegebenen Typ von:

dynamic b = (object) 2;

wird umgangen und der tatsächliche Typ wird zur Laufzeit aufgelöst.


Ich glaube, es ist viel komplizierter, als Sie sich vorstellen können, wie es zur Laufzeit aufgelöst wird .

Angenommen, Sie haben den folgenden Code:

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

und ich setze die vollständige IL (der Debug-Konfiguration) hinter meine Antwort.

Für diese beiden Zeilen:

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

die IL sind nur:

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)

Aber für diese beiden:

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

sind von IL_000f bis IL_007a von TestMethod. Es ruft die Funktion Take(object) oder Take(int) nicht direkt auf, sondern ruft die Methode folgendermaßen auf:

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

Die vollständige IL von 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
55
Ken Kin

dynamisch:

  1. dynamic ist ein Dynamically typed
  2. Dynamisch typisiert - Dies bedeutet, dass der Typ der deklarierten Variablen vom Compiler zur Laufzeit festgelegt wird.

var:

  1. var ist ein Statically typed
  2. Statisch typisiert - Dies bedeutet, dass der Typ der deklarierten Variablen beim Kompilieren vom Compiler festgelegt wird.

Auf diese Weise sehen Sie, dass die Überladungsauflösung zur Laufzeit für dynamic erfolgt.

Also gilt die Variable b als int

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

Das ist der Grund, warum Take(b);Take(int i) aufruft

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

Aber im Fall von var a = (object)2 gilt die Variable a als 'Objekt'.

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

Das ist der Grund, warum Take (a); ruft Take(object o) auf

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

Die Auflösung der Boxed Integer-Argumente erfolgt zur Kompilierungszeit. Hier ist die IL:

IL_000d:  box        [mscorlib]System.Int32
IL_0012:  stloc.0
IL_0013:  ldloc.0
IL_0014:  call       void ConsoleApp.Program::Take(object)

Sie können sehen, dass es zur Kompilierzeit selbst in der Überladung object aufgelöst wird.

Wenn Sie dynamic verwenden, wird der Laufzeitordner eingeblendet. dynamic kann nicht nur in verwaltete C # -Objekte aufgelöst werden, sondern auch in nicht verwaltete Objekte wie COM-Objekte oder JavaScript-Objekte, sofern für diese Objekte ein Laufzeitbinder vorhanden ist.

Anstatt IL anzuzeigen, zeige ich dekompilierten Code (einfacher zu lesen):

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

Sie sehen, dass die Take -Methode zur Laufzeit vom Laufzeitordner und nicht vom Compiler aufgelöst wird. Es wird also in den tatsächlichen Typ aufgelöst.

2
YK1

Wenn Sie sich die C # -Spezifikation ansehen:

1.6.6.5 Methodenüberladung

Durch das Überladen von Methoden können mehrere Methoden in derselben Klasse denselben Namen haben, solange sie eindeutige Signaturen haben. Beim Kompilieren eines Aufrufs einer überladenen Methode verwendet der Compiler eine Überladungsauflösung, um die aufzurufende Methode zu bestimmen.

Und:

7.5.4 Überprüfung der dynamischen Überlastungsauflösung während der Kompilierung

Für die meisten dynamisch gebundenen Operationen ist die Menge der möglichen Kandidaten für die Auflösung zum Zeitpunkt der Kompilierung unbekannt. In bestimmten Fällen ist der Kandidatensatz jedoch zur Kompilierungszeit bekannt:

  • Statische Methodenaufrufe mit dynamischen Argumenten

  • Instanzmethodenaufrufe, bei denen der Empfänger kein dynamischer Ausdruck ist

  • Indexer ruft auf, wenn der Empfänger kein dynamischer Ausdruck ist

  • Konstruktoraufrufe mit dynamischen Argumenten

In diesen Fällen wird für jeden Kandidaten eine Prüfung zur begrenzten Kompilierungszeit durchgeführt, um festzustellen, ob einer von ihnen möglicherweise zur Laufzeit zutrifft

In Ihrem ersten Fall ist var nicht dynamisch. Überladungsauflösung findet die Überladungsmethode um Kompilierungszeit.

Aber in Ihrem zweiten Fall rufen Sie statische Methode mit dynamischen Argumenten, Überladungsauflösung auf und finden stattdessen zur Laufzeit eine Überladungsmethode

1
cuongle

Zum besseren Verständnis der Typauflösung in Ihrem Fall für dynamische Variablen.

  • Setzen Sie zuerst einen Haltepunkt an die Linie:

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

was eindeutig bedeutet, dass der Code: dynamic b = (object)2 2 nicht in ein Objekt konvertiert, wenn er einer dynamischen Variablen zugewiesen wird und b ein int bleibt

  • Kommentieren Sie als Nächstes Ihre Überladung der Methode Take(int i) aus und setzen Sie einen Haltepunkt in die Zeile Take(b) (dasselbe gilt: Typ von b ist immer noch int) Wenn Sie es jedoch ausführen, sehen Sie, dass der gedruckte Wert wie folgt lautet: Erhaltenes Objekt.

  • Ändern Sie nun Ihren Variablenaufruf dynamic in den folgenden Code:

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

  • Ändern Sie als Nächstes Ihren Anruf in den folgenden Code und sehen Sie, was zurückgegeben wird:

    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 object.

Dies liegt daran, dass: der beste Übereinstimmungstyp für die dynamischen Variablen gemäß dem Wert aufgelöst wird, den er zur Laufzeit hat, und auch die beste aufzurufende übereinstimmende Überladungsmethode wird zur Laufzeit für dynamische Variablen aufgelöst.

1
VS1

Im ersten Fall bedeutet var object, also wird Take(object o) aufgerufen. Im zweiten Fall verwenden Sie den Typ dynamic und obwohl Sie Ihren int in ein Kästchen gesetzt haben, enthält er immer noch Informationen über den Typ -int, sodass die am besten passende Methode aufgerufen wird.

0
Guru Stron