web-dev-qa-db-fra.com

Cast dynamique et var en objet en C #

Considérez ces fonctions:

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

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

Lorsque j'appelle la fonction Take de cette façon:

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

Je reçois :Received an object

Mais si vous l'appelez comme:

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

Je reçois:Received an integer

Les deux paramètres (a & b) sont convertis en object. Mais pourquoi le compilateur a ce comportement?

52
user1968030

var n'est qu'un sucre syntaxique pour laisser le type être décidé par le RHS .

Dans votre code:

var a = (object)2;

est équivalent à:

object a = (object)2;

Vous obtenez un objet, car vous avez mis 2 Dans un objet.

Pour dynamic, vous voudrez peut-être jeter un œil à tilisation de Type dynamic . Notez que Le type est un type statique, mais un objet de type dynamique contourne la vérification de type statique , c'est-à-dire le type que vous avez spécifié:

dynamic b = (object) 2;

est contourné et son type réel est résolu lors de l'exécution.


Pour la façon dont il est résolu lors de l'exécution , je pense que c'est beaucoup plus compliqué que vous ne pouvez l'imaginer ..

Disons que vous avez le code suivant:

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

et j'ai mis l'IL complet (de configuration de débogage) à l'arrière de ma réponse.

Pour ces deux lignes:

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

les IL ne sont que:

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)

Mais pour ces deux:

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

sont de IL_000f à IL_007a de TestMethod. Il n'appelle pas directement la Take(object) ou Take(int), mais invoque la méthode comme ceci:

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

L'IL complet de 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

dynamique:

  1. dynamic est un Dynamically typed
  2. Typé dynamiquement - Cela signifie que le type de variable déclaré est décidé par le compilateur au moment de l'exécution.

var:

  1. var est un Statically typed
  2. Typé statiquement - Cela signifie que le type de variable déclaré est décidé par le compilateur au moment de la compilation.

Par cela, vous voyez que la résolution de surcharge se produit au moment de l'exécution pour dynamic.

Donc la variable b tient comme int

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

C'est la raison pour laquelle Take(b); appelle Take(int i)

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

Mais dans le cas de var a = (object)2, la variable a tient comme 'objet'

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

C'est la raison pour laquelle Take (a); appelle Take(object o)

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

La résolution d'argument entier encadré se produit au moment de la compilation. Voici l'IL:

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

Vous pouvez voir qu'il est résolu en surcharge object au moment de la compilation.

Lorsque vous utilisez dynamic - le classeur d'exécution apparaît dans l'image. dynamic non seulement peut être résolu en objets C # gérés, mais également en objets non gérés comme les objets COM ou les objets JavaScript étant donné qu'il existe un liant d'exécution pour ces objets.

Au lieu d'afficher IL, je vais montrer du code décompilé (plus facile à lire):

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

Vous voyez que la méthode Take est résolue à l'exécution par le classeur d'exécution et non par le compilateur. Ainsi, il le résoudra au type réel.

2
YK1

Si vous jetez un oeil sur la spécification C #:

1.6.6.5 surcharge de méthode

La surcharge de méthodes permet à plusieurs méthodes dans la même classe d'avoir le même nom tant qu'elles ont des signatures uniques. Lors de la compilation d'un appel d'une méthode surchargée, le compilateur utilise la résolution de surcharge pour déterminer la méthode spécifique à appeler.

Et:

7.5.4 Vérification à la compilation de la résolution de surcharge dynamique

Pour la plupart des opérations liées dynamiquement, l'ensemble des candidats possibles à la résolution est inconnu au moment de la compilation. Dans certains cas, cependant, l'ensemble candidat est connu au moment de la compilation:

  • Appels de méthode statique avec arguments dynamiques

  • Appels de méthode d'instance où le récepteur n'est pas une expression dynamique

  • Indexeur appelle lorsque le récepteur n'est pas une expression dynamique

  • Appels du constructeur avec des arguments dynamiques

Dans ces cas, une vérification limitée au moment de la compilation est effectuée pour chaque candidat pour voir si l'un d'eux pourrait éventuellement s'appliquer au moment de l'exécution

Donc, dans votre premier cas, var n'est pas dynamique, la résolution de surcharge trouvera la méthode de surcharge à à la compilation .

Mais dans votre deuxième cas, vous appelez une méthode statique avec des arguments dynamiques , une résolution de surcharge trouvera la méthode de surcharge au moment de l'exécution à la place

1
cuongle

Pour aider à comprendre la résolution de type dans votre cas pour les variables dynamiques.

  • Mettez d'abord un point d'arrêt sur la ligne:

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

ce qui signifie clairement que le code: dynamic b = (object)2 ne convertit pas 2 en objet lorsqu'il est affecté à une variable dynamique et b reste un int

  • Ensuite, commentez votre surcharge de la méthode Take(int i) puis mettez un point d'arrêt sur la ligne Take(b) (idem: le type de b est toujours int) mais lorsque vous l'exécutez, vous verrez que la valeur imprimée est: Objet reçu.

  • Maintenant, changez votre appel de variable dynamic en code ci-dessous:

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

  • Ensuite, changez votre appel en code ci-dessous et voyez ce qui est retourné:

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

Ceci est dû au fait que: le meilleur type de correspondance est résolu pour les variables dynamiques en fonction de la valeur qu'il contient au moment de l'exécution et la meilleure méthode de surcharge de correspondance à appeler est résolue au moment de l'exécution pour les variables dynamiques.

1
VS1

Dans le premier cas, var signifie object donc Take(object o) est appelé. Dans le second cas, vous utilisez le type dynamic et malgré le fait que vous ayez encadré votre int, il contient toujours des informations sur son type -int, de sorte que la meilleure méthode de correspondance est appelée.

0
Guru Stron