Приведение динамического и var к объекту в C#
рассмотрим эти функции:
static void Take(object o)
{
    Console.WriteLine("Received an object");
}
static void Take(int i)
{
    Console.WriteLine("Received an integer");
}
когда я называю таким образом:
var a = (object)2;
Take(a);
Я :Received an object
но если назвать это так:
dynamic b = (object) 2;
Take(b);
Я:Received an integer
оба параметра (a & b) приводятся к object. Но почему компилятор имеет такое поведение?
6 ответов:
varэто просто синтаксический сахар, чтобы тип был решен RHS.в коде:
var a = (object)2;эквивалентно:
object a = (object)2;вы получаете объект, так как вы коробочной
2на объект.на
dynamic, возможно, вы захотите взглянуть на использование типа dynamic. Обратите внимание, что тип является статическим типом, но объект типа 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 (конфигурации отладки) в задней части моего ответа.
для этих двух строк:
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)но для этих двух:
dynamic b=(object)2; Take(b);С
IL_000fдоIL_007aнаTestMethod. Он не вызываетTake(object)илиTake(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);
полный Ил
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[] CS00 ) 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
динамическая:
dynamicэтоDynamically typed- динамическая типизация - это означает, что тип переменной определяется компилятором во время выполнения.
var:
varэтоStatically typed- статически типизированный – это означает, что тип переменной определяется компилятором во время компиляции.
таким образом, вы видите, что разрешение перегрузки происходит во время выполнения для
dynamic.так что переменная
bдержитintdynamic 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); calls
Take(object o)static void Take(object o) { Console.WriteLine("Received an object"); }
коробочное разрешение целочисленных аргументов происходит во время компиляции. Вот Ил:
IL_000d: box [mscorlib]System.Int32 IL_0012: stloc.0 IL_0013: ldloc.0 IL_0014: call void ConsoleApp.Program::Take(object)вы можете видеть, что он разрешен в
objectперегрузка во время компиляции сам.при использовании
dynamic- связующее время выполнения входит в картину.dynamicможно разрешить не только управляемые объекты C#, но и неуправляемые объекты, такие как COM-объекты или объекты JavaScript, если для этих объектов существует связующее средство среды выполнения.вместо того, чтобы показывать IL, я покажу декомпилированный код (легче читать):
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);вы видите, что
Takeметод разрешен во время выполнения связующим средством времени выполнения, а не компилятором. Таким образом, он разрешит его до фактического типа.
Если вы посмотрите на C# спецификация:
способ 1.6.6.5 перегрузки
перегрузка метода позволяет нескольким методам в одном классе иметь одно и то же имя, если они имеют уникальные подписи. При компиляции вызова перегруженного метода компилятор использует разрешение перегрузки для определения конкретного метода для вызова.
и:
7.5.4 проверка динамического времени компиляции разрешение перегрузки
для большинства динамически связанных операций набор возможных кандидатов на разрешение неизвестен во время компиляции. В некоторых случаях, однако, набор кандидатов известен во время компиляции:
статические вызовы методов с динамическими аргументами
вызов метода экземпляра, где получатель не является динамическим выражением
индексатор вызывает, где находится приемник не динамическое выражение
вызовы конструктора с динамическими аргументами
в этих случаях для каждого кандидата выполняется ограниченная проверка времени компиляции, чтобы узнать, может ли какой-либо из них применяться во время выполнения
Итак, в вашем первом случае,
var- Это не динамичная, перегрузка разрешение найдет метод перегрузки в времени компиляции.но в вашем втором случае, вы звоню статический метод с динамическими аргументами,перегрузка разрешение найдет метод перегрузки во время выполнения вместо
чтобы помочь понять разрешение типа в вашем случае для динамических переменных.
сначала поставьте точку останова на линии:
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)(то же: типаbещеint) но когда вы запустите его, вы увидите печатное значение: получено объект.
теперь, измените ваш
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объект.это потому что: лучший тип соответствия разрешен для динамического переменные в соответствии со значением, которое он содержит во время выполнения, а также метод перегрузки наилучшего соответствия, который будет вызван, разрешается во время выполнения для динамических переменных .