2013-06-14 33 views
7

Używam protobuf-net do serializowania/deserializacji moich danych.W jaki sposób protobuf-net obsługuje tylko pola readonly?

Mam kilka raczej prostych zajęć, więc to nie jest prawdziwy problem.

O ile mi wiadomo, protobuf-net wykorzystuje generację IL do tworzenia kodu serializacji/deserializacji. Podczas gdy mam pola tylko do odczytu w moim modelu, zastanawiam się, w jaki sposób można napisać do takiego pola z IL? Mogę wyraźnie zobaczyć, że to działa dobrze, ale nie wiem dlaczego ...

Próbowałem szpiegować to w kodzie, ale to trochę zbyt skomplikowane.

Moje próby wygenerowania takiego kodu zawsze skutkują błędami walidatora IL.

Odpowiedz

7

W rzeczywistości nie mogę uzyskać tego do nieudanego - przynajmniej podczas generowania w pamięci.

Zacznijmy od prostego, z polem public readonly (więc nie łamiemy żadnych zasad dostępu); Moja pierwsza próba jest jak poniżej, i to działa dobrze:

using System; 
using System.Reflection; 
using System.Reflection.Emit; 
class Foo 
{ 
    public readonly int i; 
    public int I { get { return i; } } 
    public Foo(int i) { this.i = i; } 
} 
static class Program 
{ 
    static void Main() 
    { 
     var setter = CreateWriteAnyInt32Field(typeof(Foo), "i"); 
     var foo = new Foo(123); 
     setter(foo, 42); 
     Console.WriteLine(foo.I); // 42; 
    } 
    static Action<object, int> CreateWriteAnyInt32Field(Type type, string fieldName) 
    { 
     var field = type.GetField(fieldName, 
      BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 
     var method = new DynamicMethod("evil", null, 
      new[] { typeof(object), typeof(int) }); 
     var il = method.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Castclass, type); 
     il.Emit(OpCodes.Ldarg_1); 
     il.Emit(OpCodes.Stfld, field); 
     il.Emit(OpCodes.Ret); 
     return (Action<object, int>)method.CreateDelegate(typeof(Action<object, int>)); 
    } 
} 

Tylko raz robi się ciekawie jest, jeśli pole jest private:

private readonly int i; 

Powyższy kod następnie daje jakże niejasne :

Operacja może zdestabilizować środowisko wykonawcze.

Ale obejść, że udając, że metoda jest wewnątrz pola typ uznającej:

var method = new DynamicMethod("evil", null, 
    new[] { typeof(object), typeof(int) }, field.DeclaringType); 

niektórych innych kontroli wewnętrznych można zrobić poprzez umożliwienie skipVisibility:

var method = new DynamicMethod("evil", null, 
    new[] { typeof(object), typeof(int) }, field.DeclaringType, true); 

jednak zauważ, że nie wszystko to jest możliwe w przypadku generowania samodzielnych złożeń. Podczas tworzenia rzeczywistych bibliotek dll są utrzymywane znacznie wyższe standardy. Z tego powodu narzędzie precompiler (do wstępnego generowania złożeń) nie może obsłużyć dość tego samego zakresu scenariuszy, co kod metaprogramowania w pamięci.

+0

Sprawdzę to od razu. Może mój problem nie był ściśle związany z polami tylko do odczytu. Zabawne jest to, że mój kod działa na Mono i nie działa na platformie .NET. –

+0

Doh. Nie udało mi się podać DeclaringType. Ale teraz zastanawiam się - dlaczego to działa? Czy nie powinno się zapisywać pola tylko do odczytu z konstruktora obiektu? Dzięki za odpowiedź. –

+0

@PiotrZierhoffer wiele rzeczy jest egzekwowanych tylko w kompilatorze iw pełnym 'PEVerify'. Ostatecznie, możesz zmienić pola 'readonly' przez odbicie, a serializatory/materializatory często ** całkowicie pomijają ** konstruktora, więc gdyby tak było, nie byłoby możliwości przypisania wartości –

3

Ponieważ jestem dość zainteresowany tą dyskusją, wypróbowałem przykładowy kod Marca Gravella i ... rzuca VerificationException na MS .NET 4.0.

udało mi się zrobić to praca, ale potrzebne do korzystania DynamicMethod konstruktora z owner zestawu parametrów do field.DeclaringType nawet w przypadku publicznego i dziedzinie. SkipVisibility Parametr wydaje się w tym przypadku nadmiarowy.

PS. Uważam, że ten wpis powinien być komentarzem, ale z powodu braku przedstawiciela nie jestem w stanie wypowiedzieć się na temat innych odpowiedzi.

+0

Cóż, nie uruchomiłem kodu Marca, tylko go analizowałem, ale punkt wydaje się być poprawny. –

Powiązane problemy