I use protobuf-net to serialize/deserialize my data.
I have some rather simple classes, so that's no real problem.
As far as I know, protobuf-net uses IL generation to create serialization/deserialization code. While I have readonly fields in my model, I wonder how is it possible to write to such a field with IL? I can plainly see it works well, but I don't know why...
I've tried to spy it in the code, but it's a bit too complicated.
My attempts to generate such code myself always result in IL validator errors.
Actually, I can't get it to fail - at least, when generating in memory.
Let's start simply, with a public readonly
field (so we aren't breaking any accessebility rules); my first attempt is as below, and it works fine:
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>));
}
}
The only time it gets interesting is if the field is private
:
private readonly int i;
The code above then gives the oh-so-vague:
Operation could destabilize the runtime.
But we get around that by pretending that the method is inside the field's declaring type:
var method = new DynamicMethod("evil", null,
new[] { typeof(object), typeof(int) }, field.DeclaringType);
Some other internal checks can be done by enabling skipVisibility
:
var method = new DynamicMethod("evil", null,
new[] { typeof(object), typeof(int) }, field.DeclaringType, true);
However, note that not all of this is possible if generating standalone assemblies. You are held to much higher standards when creating actual dlls. For this reason, the precompiler
tool (to pre-generate assemblies) cannot handle quite the same range of scenarios that the in-memory meta-programming code can.
As I am quite interested in this discussion, I have tried Marc Gravell's example code and... it throws VerificationException
on MS .NET 4.0.
I've managed to make it work but I needed to use DynamicMethod
constructor with owner
parameter set to field.DeclaringType
even in case of public i
field. SkipVisibility
parameter seems to be redundant in this case.
PS. I believe that this entry should be a comment, but due to the lack of rep I'm unable to comment other's answers.