2012-03-16 4 views
0

私は、同じオブジェクトグラフを複数のスレッドにまたがって深くクローンすることを含むプログラムの特定の部分のパフォーマンスを向上させようとしています。現在のところ、シンプルな実装であるシリアライゼーションを使用していますが、もっと速いものが必要です。私はILクローニングのアイデアを見つけ、here (Whizzo's Blog)というコードで作業しようとしています。C#Reflection IL - 値のコピー方法の理解

私は本当にまだと​​してILを得ることはありませんので、私は誰かが少し手助けして(私はこれは、いくつかの最初の質問であると想像)私にはもののいくつかを説明することができます願っています。

opcodeとreflection.emitを説明する良いリンクがあれば誰でも良いでしょう。MSDNでは詳細はあまりありません)どのように値がコピーされていますか?私は、新しいオブジェクトを構築し、スタックその後少し後に関心のフィールド値を指定し、値が何らかの形でコピーされ

generator.Emit(OpCodes.Newobj, cInfo); 
generator.Emit(OpCodes.Stloc, cloneVariable); 

からポップされていることがわかります。私は元のオブジェクトに戻って元のオブジェクトが参照されていないように見えるときに、そのオブジェクトを取得する方法を理解していません。それともこれは(私はそれが何をするか、100%わからない)LocalBuilderのいくつかの魔法です:

// I *THINK* this Pushes the cloneVariable on the stack, loads an argument (from where?) and sets the field value based on the FieldInfo?? 
generator.Emit(OpCodes.Ldloc, cloneVariable); 
generator.Emit(OpCodes.Ldarg_0); 
generator.Emit(OpCodes.Ldfld, field); 
generator.Emit(OpCodes.Stfld, field); 

私はいつもディープクローンをしたいと私はそれがシリアル化されたフィールドに基づいて欲しいと、私は少しのコードを変更しました:あなたが持っている

private static T CloneObjectWithILDeep(T myObject) 
{ 
    Delegate myExec = null; 
    if (!_cachedILDeep.TryGetValue(typeof(T), out myExec)) 
    { 
    // Create ILGenerator    
    DynamicMethod dymMethod = new DynamicMethod("DoDeepClone", typeof(T), new Type[] { typeof(T) }, Assembly.GetExecutingAssembly().ManifestModule, true); 
    ILGenerator generator = dymMethod.GetILGenerator(); 
    LocalBuilder cloneVariable = generator.DeclareLocal(myObject.GetType()); 

    ConstructorInfo cInfo = myObject.GetType().GetConstructor(Type.EmptyTypes); 
    generator.Emit(OpCodes.Newobj, cInfo); 
    generator.Emit(OpCodes.Stloc, cloneVariable); 

    foreach (FieldInfo field in typeof(T).GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public)) 
    {   
     if(field.IsNotSerialized) 
      continue; 

     if (field.FieldType.IsValueType || field.FieldType == typeof(string)) 
     { 
      generator.Emit(OpCodes.Ldloc, cloneVariable); 
      generator.Emit(OpCodes.Ldarg_0); 
      generator.Emit(OpCodes.Ldfld, field); 
      generator.Emit(OpCodes.Stfld, field); 
     } 
     else if (field.FieldType.IsClass) 
     { 
      CopyReferenceType(generator, cloneVariable, field); 
     } 
     } 

     generator.Emit(OpCodes.Ldloc_0); 
     generator.Emit(OpCodes.Ret); 
     myExec = dymMethod.CreateDelegate(typeof(Func<T, T>)); 
     _cachedILDeep.Add(typeof(T), myExec); 
    } 
    return ((Func<T, T>)myExec)(myObject); 
    } 
+0

。 –

+0

C#でしようとしていることを表現できますか? – thecoop

+0

"同じオブジェクトグラフを繰り返し深くクローンする"本当にそれを行う必要がありますか?それをもっと速くしようとするのではなく、何とかそれを回避することはできませんか? – svick

答えて

3

まず

generator.Emit(OpCodes.Ldloc, cloneVariable); 
generator.Emit(OpCodes.Ldarg_0); 

あなたのスタックが(cloneVariable、myObjectという)が含まれてい

enerator.Emit(OpCodes.Ldfld, field); 

この1つのポップは、参照オブジェクト、ポップ、基準オブジェクトとこれをフィールドから値を取得し、スタックに値をプッシュ

あなたのスタック(cloneVariable、myObject.field)

generator.Emit(OpCodes.Stfld, field); 

が含まその値をオブジェクトのフィールドに格納します。結果は、C#の

cloneVariable.field = myObject.field 
+0

ええ、いつmyObjectがスタックに乗ったのですか?私はそれがおそらく私が行方不明だと思う! – Ian

+0

'ldarg_0'は、メソッドの引数0をスタックにロードすることを意味します。メソッドの引数が1つしかなく、メソッドが 'static'なので、この引数のインデックスは0です(インスタンスメソッドでは' this'引数のために0が予約されているので、インスタンスメソッドでは1になります) –

0

¿の代わりにクラスの構造体を使用する方が簡単ではありませんし、直接マーシャル新しいオブジェクトのメモリへのタイプのバイト配列に相当しているのですか?

マーシャルではまっすぐにやることができますが、私はほとんど(常にドアを開けたままにしてください) - 安全でコンパイルして、ポインタをキャストすることができますto byte *をコピーし、それらのバイトをターゲット構造体ポインタにコピーします。

は、[編集]あなただけのマーシャリングにより、安全でないことなく、それを行うことができ、ポインタを忘れます。この記事をチェックする。

How to convert a structure to a byte array in C#?

[EDIT2]私はいくつかの忍耐を学ばなければならない:Pあなたのソリューションは、はるかに高速です。オペコードは、CLRのECMA仕様に記述されているけど、あなたはこのようなものをやっている場合、私は非常にMSILアセンブラにセルジュLidinの最新の著書を取得をお勧め何