2013-06-14 7 views
7

私はprotobuf-netを使って自分のデータをシリアル化/逆シリアル化します。protobuf-netはどのように読み取り専用フィールドを扱うのですか?

私はいくつかの単純なクラスを持っているので、実際の問題はありません。

は、私の知る限りでは、いるProtobuf-netはシリアライズ/デシリアライズコードを作成するために、ILの生成を使用しています。私のモデルには読み込み専用のフィールドがありますが、ILでどのようにこのようなフィールドに書き込むことができるのでしょうか?私ははっきりそれがうまく機能見ることができますが、私はコードでそれをスパイしようとした理由は...

知りませんが、それは少しも複雑です。

このようなコードを自分で作成しようとすると、常にILバリデータエラーが発生します。

答えて

7

実際には、私はに失敗する可能性があります。少なくとも、メモリに生成すると、に失敗します。

のは、(ので、我々はすべてのaccessebilityルールを破っていない)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; 

上記のコードその後、OH-SO-漠然とを与えます:

操作は、ランタイムを不安定化させることができます。

しかし、我々はこの方法は、フィールドの宣言型の内側にあることをふりをすることにより、その回避:

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を作成する際には、より高い基準が適用されます。このため、(アセンブリをあらかじめ生成するための)ツールでは、メモリ内メタプログラミングコードで可能なシナリオと全く同じ範囲を処理できません。

+0

すぐに確認します。たぶん私の問題は厳密に読み取り専用フィールドに関連していなかったでしょう。面白いことに、私のコードはMonoで動作し、.NETでは動作しません。 –

+0

Doh。私はDeclaringTypeを提供できませんでした。しかし今、私はそれがなぜ機能するのだろうかと思います。オブジェクトのコンストラクタからのみ読み込み可能フィールドに書き込み可能にすべきではありませんか?とにかく答えてくれてありがとう。 –

+0

@PiotrZierhofferコンパイラやPEVerifyでのみ多くのことが強制されます。最終的には、リフレクションを介して 'readonly'フィールドを変更することができますし、シリアライザ/マテリアライザーは**コンストラクタを完全にスキップします**その場合、値を代入する方法はありません –

3

私はこの議論では非常に興味がありますように、私はマルクGravellのコード例を試してみましたと...それはMS .NET 4.0にVerificationExceptionをスローします。

私はそれを動作させることができたが、私も、公共iフィールドの場合にfield.DeclaringTypeに設定ownerパラメータでDynamicMethodコンストラクタを使用する必要がありました。この場合、SkipVisibilityのパラメータは重複しているようです。

PS。私はこのエントリーはコメントでなければならないと信じていますが、担当者の不足のために私は他人の答えにコメントすることができません。

+0

まあ、私はマークのコードを実行していない、それだけを分析したが、その点は有効であるようだ。 –

関連する問題