protobuf-net如何处理只读字段?

时间:2013-06-14 13:38:16

标签: c# protobuf-net readonly il

我使用protobuf-net来序列化/反序列化我的数据。

我有一些相当简单的课程,所以没有真正的问题。

据我所知,protobuf-net使用IL生成来创建序列化/反序列化代码。虽然我的模型中只有字段,但我想知道用IL写这样一个字段怎么可能?我可以清楚地看到它运作良好,但我不知道为什么......

我试图在代码中窥探它,但它太复杂了。

我自己尝试生成此类代码总是会导致IL验证程序错误。

2 个答案:

答案 0 :(得分:7)

实际上,我无法让它失败 - 至少在内存中生成时。

让我们简单地开始,使用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);

但请注意,如果生成独立程序集,并非所有这些都可行。在创建实际dll时,您将遵循更高的标准。因此,precompiler工具(预生成程序集)无法处理与内存中元编程代码完全相同的场景。

答案 1 :(得分:3)

由于我对这个讨论很感兴趣,我尝试过Marc Gravell的示例代码,并且......在MS .NET 4.0上抛出VerificationException

我已设法使其工作但我需要使用DynamicMethod构造函数,owner参数设置为field.DeclaringType,即使是公共i字段。在这种情况下,SkipVisibility参数似乎是多余的。

PS。我认为此条目应该是评论,但由于缺乏代表,我无法评论其他人的答案。

相关问题