Лиття динамічних і var до об'єкта в C #

Розглянемо ці функції:

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

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

Коли я називаю функцію Take таким чином:

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

I get :Received an object

Але якщо називати це як:

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

I get:Received an integer

Both parameters (a & b) are cast to object. But why compiler has this behavior?

52
a має тип компіляції object . Це звичайний тип статичного C#, тому прив'язка до певного перевантаження відбувається під час компіляції залежно від типу часу компіляції. Оскільки це об'єкт , це простий вибір. З іншого боку, b має тип компіляції типу dynamic . Бокс до об'єкта є зайвим. Пам'ятайте, що dynamic дорівнює object , тільки при обов'язковому відкладеному до виконання часу. Вся ідея з dynamic полягає в тому, щоб не стежити за типом часу компіляції, а потім почекати з обов'язковим для виконання runtime. Оскільки тип runtime Int32 , знову це легко.
додано Автор Jeppe Stig Nielsen, джерело
додано Автор Peyman Tahmasebi, джерело

6 Відповіді

var is just a syntactic sugar to let the type to be decided by the RHS.

У вашому коді:

var a = (object)2;

еквівалентно:

object a = (object)2;

Ви отримаєте об'єкт, оскільки ви вставили 2 до об'єкта.

Для динамічного ви можете подивитися на Використання Тип динамічного . Зверніть увагу, що Тип є статичним типом, але об'єкт типу динамічний обходить статичний тип перевірки , тобто тип, який ви вказали:

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_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 (об'єкт) або Take (int) безпосередньо, але викликає спосіб таким способом:

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.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null),
        CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
    }));
}
TestClass.o__SiteContainer0.<>p__Site1.Target(TestClass.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 '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> '<>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(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> 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.CompilerServices.CallSite`1 class [System.Core]System.Runtime.CompilerServices.CallSite`1>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)
        IL_0059: stsfld class [System.Core]System.Runtime.CompilerServices.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.CompilerServices.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
56
додано
Розподіл складних перевантажень однаковий для динамічного порівняно з звичайним нединамічним подібним до об'єкта об'єктом. Єдина відмінність полягає в тому, що для dynamic , цей складний алгоритм, який приймає рішення про перевантаження, відбувається не до часу виконання на основі фактичного типу виконання (наприклад, вихід GetType() ). . З нединамичним типом таке саме зв'язування відбувається під час компіляції, виходячи з типу оголошеного (тип часу компіляції) змінної або виразу.
додано Автор Jeppe Stig Nielsen, джерело
Для роздільної перевантаження є хороше пояснення щодо вікі - C Sharp 4.0 .
додано Автор Ken Kin, джерело
Імена згенерованих компіляторами мають вигляд відповіді р-н Ліперт: stackoverflow.com/questions/2508828/ …
додано Автор Ken Kin, джерело

динамічний:

  1. dynamic is a Dynamically typed
  2. Dynamically typed - This means the type of variable declared is decided by the compiler at run time.

var:

  1. var is a Statically typed
  2. Statically typed – This means the type of variable declared is decided by the compiler at compile time.

Таким чином, ви бачите, що роздільна здатність перевантаження виникає під час виконання dynamic .

So variable b holds as int

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

That's the reason why Take(b); calls Take(int i)

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

Але у випадку var a = (об'єкт) 2 змінна a тримається як "об'єкт"

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

That's the reason why Take(a); calls Take(object o)

static void Take(object o)
    {
        Console.WriteLine("Received an object");
    }
16
додано
Номер 2 на етапі відтворення відтворюється до цілого, через характер динаміки. Здається, що динамічна ігнорує явне подання.
додано Автор jnovacho, джерело
@SivaCharan Чому dynamic b = (об'єкт) 2; тримайте int ?
додано Автор Dimitar Dimitrov, джерело
@СіваCharan Хороший матеріал, піднятий.
додано Автор Dimitar Dimitrov, джерело
@ YK1 Спасибі, я зараз це отримую, a.GetType() вирішується до System.Int32 під час виконання натисніть :)
додано Автор Dimitar Dimitrov, джерело
@SivaCharan я бачив це, однак, оскільки з часу перезавантаження відбувається роздільна перезавантаження, чому це не вирішується замість об'єкта ? Я не бачу, як ваш пояснює це, мате
додано Автор Dimitar Dimitrov, джерело
Ми розуміємо, що це виклики Take (int i) - це питання -> чому?
додано Автор Dimitar Dimitrov, джерело
@ppejovic Ні, це не просто, ти, я прочитав це 10 разів, не бачу пояснення, але, можливо, я просто непристойний ...
додано Автор Dimitar Dimitrov, джерело
Ваше пояснення dynamic не є правильним: тип змінної об'єкт . Проте, коли ця змінна використовується у виразі, це призведе до виклику компілятора під час виконання, а також для виконання оцінки виразу, що базується на наявних типів виконання (грубо говорячи.)
додано Автор dlev, джерело
@DimitarDimitrov: Я вже заявив, що "Резервування перевантаження виникає під час роботи"
додано Автор Siva Charan, джерело
@ Димиттар Дімітров: Яка складність зрозуміти?
додано Автор Siva Charan, джерело
@ Димиттар Дімітров: Вибачте за неправильно зрозуміле. Я зрозумів вашу думку, тепер я оновив відповідь.
додано Автор Siva Charan, джерело
@DimitarDimitrov: Оскільки воно вирішено під час виконання.
додано Автор Siva Charan, джерело
Чи це мені чи це не відповідає дійсно на питання?
додано Автор Pero P., джерело
@DimitarDimitrov: екземпляр об'єкта може мати лише один тип під час виконання. Не просто dynamic , але також об'єкта a = (об'єкт) 2; a.GetType (); дасть вам System.Int32 в runtime . Просто перша роздільна перевантаження була виконана компілятором під час компіляції, отже, вона вирішує перевантажувати object .
додано Автор YK1, джерело
Компілятор під час виконання , як комбінатор може бути викликаний під час виконання, це може бути інтерпретатор C# VM або C#, який вирішує тип цієї динамічної змінної.
додано Автор NeeL, джерело

Рішення з аргументованим цілим числом аргументів відбувається під час компіляції. Ось IL:

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

Ви можете побачити, що він вирішив перезавантажити об'єкт під час компіляції.

Коли ви використовуєте dynamic - зв'язування runtime входить у зображення. dynamic не тільки може вирішити керовані об'єкти C#, але й об'єкти, що не мають керування, такі як COM-об'єкти або об'єкти JavaScript, якщо для цих об'єктів існує зв'язування runtime.

Замість того, щоб показувати IL, я покажу декомпілювати код (простіше читати):

   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.UseCompileTimeType, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); }
o__SiteContainer0.<>p__Site1.Target(
o__SiteContainer0.<>p__Site1, typeof(Program), obj3);

Ви бачите, що метод Take вирішується під час виконання тимчасовим сполученням, а не компілятором. Таким чином, це дозволить вирішити його до фактичного типу.

3
додано

Якщо ви подивитеся на специфікації C #:

1.6.6.5 Перевантаження методу

Перевантаження методу дозволяє декілька методів одного і того ж класу мати однакове ім'я, якщо вони мають унікальні підписи. Під час складання виклику перевантаженого методу компілятор використовує роздільну здатність перевантаження, щоб визначити конкретний спосіб викликати.

І:

7.5.4 Перевірка динамічної перевантаженості під час компіляції

Для більшості динамічно пов'язаних операцій набір можливих кандидатів на вирішення невідомий під час компіляції. У деяких випадках, однак, кандидат встановлений відомий під час компіляції:

     
      
  • Виклики статичного методу з динамічними аргументами

  •   
  • Виклик методу виклику, де приймач не є динамічним вираженням

  •   
  • Індексатор викликів, де приймач не є динамічною експресією

  •   
  • Конструктор викликає динамічні аргументи

  •   
     

У цих випадках для кожного кандидата проводиться обмежена перевірка часу компіляції, щоб дізнатися, чи може будь-який з них бути застосований під час виконання

Отже, у вашому першому випадку var не є динамічним, роздільна здатність перевантаження знайде метод перевантаження на час компіляції .

Але у вашому другому випадку ви викликаєте статичний метод з динамічними аргументами , роздільна здатність перевантаження знайде метод перевантаження на час виконання

2
додано
Я думаю, що питання полягає в тому, коли воно пов'язане з виконанням часу .. чому явний вимовлений об'єкт не набуває чинності?
додано Автор Simon Whitehead, джерело
@SimonWhitehead: Я вважаю, що ця відповідь корисна щодо роздільної здатності перевантаження, яка явно не відображається в інших відповідях, навіть моєму.
додано Автор Ken Kin, джерело

Щоб допомогти зрозуміти вирішення типу динамічних змінних у вашому випадку.

  • First put a break point at the line:

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

, що явно означає, що код: dynamic b = (об'єкт) 2 не перетворюється   2 до об'єкта при призначенні динамічної змінної і b залишається int

  • Next, comment out your overload of Take(int i) method and then put a break point on line Take(b) (same: type of b is still int) but when you run it, you will see printed value is: Recieved object.

  • Now, change your dynamic variable call to below code:

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

  • Next, change your call to below code and see what is returned:

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

Це пов'язано з тим, що: найкращий тип відповідності вирішується для динамічного   змінні відповідно до значення, яке воно зберігає під час виконання, а також найбільш підходящий спосіб перевантаження, який буде викликаний, вирішується під час виконання для динамічних змінних.

2
додано

У першому випадку var означає object , тому викликається Take (object o) . У другому випадку ви використовуєте тип dynamic , і незважаючи на те, що ви вставили int у нього все ще є інформація про його тип - int , щоб найкраще відповідати метод називається.

1
додано
var chat = new Chat();
var chat = new Chat();
642 учасників

Обсуждение вопросов по C# / .NET / .NET Core / .NET Standard / Azure Сообщества-организаторы: — @itkpi — @dncuug