2013-06-14 2 views
7

Я использую protobuf-net для сериализации/десериализации данных.Как обрабатывать поля protobuf-net только для чтения?

У меня есть довольно простые классы, так что это не проблема.

Насколько я знаю, protobuf-net использует генерацию IL для создания кода сериализации/десериализации. Хотя у меня есть только поля в моей модели, интересно, как можно писать в такое поле с помощью IL? Я могу ясно видеть, что это работает хорошо, но я не знаю, почему ...

Я пытался заглянуть в код, но это слишком сложно.

Мои попытки генерировать такой код сами всегда приводят к ошибкам проверки IL.

ответ

7

На самом деле, я не могу получить его до fail - по крайней мере, при генерации в памяти.

Начнем с поля public readonly (поэтому мы не нарушаем никаких правил доступности); моя первая попытка, как показано ниже, и она отлично работает:

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>)); 
    } 
} 

Единственный раз, когда это становится интересным, если поле private:

private readonly int i; 

код выше, то дает о так расплывчато :

Операция может дестабилизировать время выполнения.

Но мы получаем вокруг этого, делая вид, что метод находится внутри типа объявляющего месторождения:

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

некоторых других внутренних проверок может быть сделано путем предоставления skipVisibility:

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

Однако, обратите внимание, что не все это возможно при создании автономных сборок. При создании фактических библиотек вы придерживаетесь гораздо более высоких стандартов. По этой причине инструмент precompiler (для предварительной генерации сборок) не может обрабатывать тот же диапазон сценариев, что и код метапрограммирования в памяти.

+0

Я проверю его сразу. Возможно, моя проблема не была строго связана с полями readonly. Самое забавное, что мой код работает на Mono и не работает на .NET. –

+0

Дох. Мне не удалось предоставить DeclaringType. Но теперь мне интересно - почему это работает? Должно ли поле readonly записываться только из конструктора объекта? Спасибо за ответ, так или иначе. –

+0

@PiotrZierhoffer многие вещи применяются только в компиляторе и в полном объеме «PEVerify». В конечном счете, вы можете изменять поля 'readonly' через отражение, а сериализаторы/материализаторы часто ** полностью пропускают ** конструктор, поэтому, если бы это было так, то было бы невозможно назначить значение –

3

Как я очень заинтересован в этом обсуждении, я пробовал примерный код Марка Гравелла и ... он бросает VerificationException на MS .NET 4.0.

мне удалось заставить его работать, но мне нужно использовать DynamicMethod конструктор с owner набора параметров для field.DeclaringType даже в случае публичного i поля. В этом случае параметр SkipVisibility представляется избыточным.

PS. Я считаю, что эта запись должна быть комментарием, но из-за отсутствия репутации я не могу прокомментировать ответы других.

+0

Ну, я не запустил код Марка, только проанализировал его, но точка кажется действительной. –