Приведение динамического и 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
держит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); 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
объект.
это потому что: лучший тип соответствия разрешен для динамического переменные в соответствии со значением, которое он содержит во время выполнения, а также метод перегрузки наилучшего соответствия, который будет вызван, разрешается во время выполнения для динамических переменных .