Casting dynamic et var en object 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"); } 

Quand j’appelle la fonction Take cette façon:

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

Je reçois: a Received an object

Mais si appelez ça comme:

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

J’obtiens: Received an integer

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

var est juste 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 object, puisque vous avez mis 2 dans un object.

Pour le mode dynamic , vous pouvez consulter le document Utilisation du type dynamic . Notez que Le type est un type statique, mais qu’un object de type dynamic ignore la vérification de type statique , c’est-à-dire le type que vous avez spécifié:

 dynamic b = (object) 2; 

est ignoré, et le type réel est résolu au moment de l’exécution.


Pour ce qui est de sa résolution à 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 je mets l’intégralité de l’IL (de la 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 Take(object) ou Take(int) , mais appelle la méthode comme ceci:

 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.UseComstackTimeType | CSharpArgumentInfoFlags.IsStaticType, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); } TestClass.o__SiteContainer0.<>p__Site1.Target(TestClass.o__SiteContainer0.<>p__Site1, typeof(TestClass), b); 

L’IL complète de TestClass :

 .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.ComstackrServices.ComstackrGeneratedAtsortingbute::.ctor() = ( 01 00 00 00 ) // Fields .field public static class [System.Core]System.Runtime.ComstackrServices.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(ssortingng) 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(ssortingng) 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.ComstackrServices.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.ComstackrServices.CallSite`1 class [System.Core]System.Runtime.ComstackrServices.CallSite`1>::Create(class [System.Core]System.Runtime.ComstackrServices.CallSiteBinder) IL_0059: stsfld class [System.Core]System.Runtime.ComstackrServices.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.ComstackrServices.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 

dynamic:

  1. dynamic est un Dynamically typed
  2. Dactylographié dynamicment – 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. Typiquement typé – Cela signifie que le type de variable déclaré est décidé par le compilateur au moment de la compilation.

De cette façon, vous voyez que la résolution de la surcharge se produit au moment de l’exécution pour la dynamic .

Donc la variable b tient comme int

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

C’est la raison pour laquelle Take(b); appels 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 est a “object”

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

C’est la raison pour laquelle Take (a); appels Take(object o)

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

La résolution des arguments entiers encadrés 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 à la surcharge de l’ object au moment de la compilation lui-même.

Lorsque vous utilisez la dynamic , le classeur d’exécution apparaît. dynamic peut non seulement résoudre les objects C # gérés, mais également les objects non gérés tels que les objects COM ou les objects JavaScript, à condition qu’un classeur d’exécution existe pour ces objects.

Au lieu de montrer IL, je montrerai le code décompilé (plus facile à lire):

  object obj3 = 2; if (
o__SiteContainer0.<>p__Site1 == null) {
o__SiteContainer0.<>p__Site1 = CallSite>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "Take", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseComstackTimeType, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); }
o__SiteContainer0.<>p__Site1.Target(
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. Donc, il va résoudre le problème au type actuel.

Si vous regardez la spécification C #:

1.6.6.5 Surcharge de méthode

La surcharge de méthode permet à plusieurs méthodes de 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 une résolution de surcharge pour déterminer la méthode spécifique à appeler.

Et:

7.5.4 Comstackr au moment de la vérification de la résolution dynamic de la surcharge

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

  • Appels de méthodes statiques avec des arguments dynamics

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

  • Appels d’indexeur où le récepteur n’est pas une expression dynamic

  • Appels de constructeur avec des arguments dynamics

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

Donc, dans votre premier cas, var n’est pas dynamic, la résolution de la surcharge trouvera la méthode de la surcharge au moment de la compilation .

Mais dans votre second cas, vous appelez la méthode statique avec des arguments dynamics , la résolution de la surcharge trouvera plutôt la méthode de la surcharge à l’exécution

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

  • Mettez d’abord un point d’arrêt à 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 object lorsqu’il est assigné à une variable dynamic et b rest un int

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

  • Maintenant, modifiez votre appel de variable dynamic au code ci-dessous:

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

  • Ensuite, changez votre appel au 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 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 .

En effet, le meilleur type de correspondance est résolu pour les variables dynamics en fonction de la valeur qu’il contient lors de l’exécution et la meilleure méthode de surcharge à appeler est résolue à l’exécution pour les variables dynamics.

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