W jaki sposób deserializacja WCF tworzy wystąpienia obiektów bez wywoływania konstruktora?

79

Podczas deserializacji WCF zachodzi pewna magia. W jaki sposób tworzy wystąpienie typu kontraktu danych bez wywoływania jego konstruktora?

Rozważmy na przykład ten kontrakt danych:

[DataContract]
public sealed class CreateMe
{
   [DataMember] private readonly string _name;
   [DataMember] private readonly int _age;
   private readonly bool _wasConstructorCalled;

   public CreateMe()
   {
      _wasConstructorCalled = true;
   }

   // ... other members here
}

Po uzyskaniu instancji tego obiektu za pośrednictwem DataContractSerializerzobaczysz, że to pole _wasConstructorCalledjest false.

Jak więc to robi WCF? Czy jest to technika, której mogą używać inni, czy też jest ona przed nami ukryta?

Drew Noakes
źródło

Odpowiedzi:

102

FormatterServices.GetUninitializedObject()utworzy instancję bez wywoływania konstruktora. Znalazłem tę klasę, używając Reflectora i przeglądając niektóre z podstawowych klas serializacji .Net.

Przetestowałem to używając przykładowego kodu poniżej i wygląda na to, że działa świetnie:

using System;
using System.Reflection;
using System.Runtime.Serialization;

namespace NoConstructorThingy
{
    class Program
    {
        static void Main()
        {
            // does not call ctor
            var myClass = (MyClass)FormatterServices.GetUninitializedObject(typeof(MyClass));

            Console.WriteLine(myClass.One); // writes "0", constructor not called
            Console.WriteLine(myClass.Two); // writes "0", field initializer not called
        }
    }

    public class MyClass
    {
        public MyClass()
        {
            Console.WriteLine("MyClass ctor called.");
            One = 1;
        }

        public int One { get; private set; }
        public readonly int Two = 2;
    }
}

http://d3j5vwomefv46c.cloudfront.net/photos/large/687556261.png

Jason Jackson
źródło
6
Cóż, wcześniej zamieściłem złą odpowiedź (teraz usuniętą), więc poczułem się winny. Nie ma to jak zrujnować ego programisty, aby skłonić go do przeprowadzenia pewnych badań.
Jason Jackson
3
Czy ktoś inny zastanawia się teraz, jak działa FormatterServices.GetUninitializedObject? Odbicie?
harpo
Jeśli sobie przypomnę, to wezwanie do kodu natywnego. Nie mogłem podążać dalej w dół króliczej nory z Reflektorem.
Jason Jackson
6
Dziwne - uruchomiłem ten kod w linqpadzie i otrzymałem: 0 0 jako wyjście. Właściwie to ma dla mnie sens, ponieważ inicjatory pola są wbudowane w ctors AFAIK
bushed
1
@bushed jest poprawne. Mam opublikował zrzut ekranu z kodem i prowadzić tutaj . Na początku myślałem, że to może być różnica w wersjach frameworka .NET (ponieważ odpowiedź ma już 4 lata), ale sprawdziłem, czy jest to 2.0 i 4.0 i oba piszą 0 i 0 do konsoli. Jason Jackson, czy mógłbyś zaktualizować swój post, aby odzwierciedlić te ustalenia?
Oliver