2010-12-07 7 views
3

私は次のプロパティを書き換えたい:これまでMono.CecilでSystem.Object.Equalsへの呼び出しを挿入する方法? Mono.Cecilを使用して

public string FirstName 
{ 
    get { return _FirstName; } 
    set 
    { 
     _FirstName = value; 
    } 
} 

public string FirstName 
{ 
    get { return _FirstName; } 
    set 
    { 
     if (System.Object.Equals(_FirstName, value)) 
     { 
      return; 
     } 
     _FirstName = value; 
    } 
} 

これは、リライトがどうなるかのほんの一部ですが、私がいるところではあります問題。

Reflectorを使用すると、次のコードではSystem.Object.Equals()の呼び出しを除き、必要に応じてプロパティを書き換えることができます。

call bool [mscorlib]System.Object::Equals(object, object) 

が、それはのように書かれている:ILコードがあることを期待している場合

call instance void RewriteSharp.Person::.ctor() 

System.Object.Equalsへの呼び出しを記述するためのコードは次のとおりです。

setMethodWriter.InsertBefore(
    firstExistingInstruction, 
    setMethodWriter.Create(OpCodes.Call, objectEqualsMethodReference)); 

objectEqualsMethodReferenceを初期化するために使用されるメソッドは、次のとおりです。

private static MethodReference GetSystemObjectEqualsMethodReference(
    AssemblyDefinition assembly 
) 
{ 

    var typeReference = assembly.MainModule.GetTypeReferences() 
     .Single(t => t.FullName == "System.Object"); 

    var typeDefinition = typeReference.Resolve(); 

    var methodDefinition = typeDefinition.Methods.Single(
          m => m.Name == "Equals" 
           && m.Parameters.Count == 2 
           && m.Parameters[0].ParameterType.Name == "Object" 
           && m.Parameters[1].ParameterType.Name == "Object" 
    ); 

    return methodDefinition; 
} 

私はsetMethodWriter.Create()またはGetSystemObjectEqualsMethodReference()が間違っていて、デバッグの量が問題を解決していないようです。

書き込まれているプロパティとプロパティを書き換えるコードは同じフレームワークターゲットです。 3.5と4.0の両方が失敗します。

私はマスターブランチhttps://github.com/jbevain/cecilを使用してMono.Cecilを構築しています。

完全なコードリスト

using Mono.Cecil; 
using Mono.Cecil.Cil; 
using System; 
using System.Linq; 

namespace RewriteNotifyPropertyChanged 
{ 
class Program 
{ 
static void Main(string[] args) 
{ 
    var rewrite = "..\\RewriteSharp.dll"; 
    var rewritten = "..\\RewritenSharp.dll"; 

    var typeName = "Person"; 
    var propertyName = "FirstName"; 

    var assembly = AssemblyDefinition.ReadAssembly(rewrite); 
    var typeDefinition = assembly.MainModule.Types.Single(t => t.Name == typeName); 
    var propertyDefintion = typeDefinition.Properties 
     .Single(p => p.Name == propertyName); 

    var setMethodWriter = propertyDefintion.SetMethod.Body.GetILProcessor(); 
    var backingFieldReference = GetBackingFieldReference(typeDefinition, propertyName); 
    var objectEqualsMethodReference = GetSystemObjectEqualsMethodReference(assembly); 
    var firstExistingInstruction = setMethodWriter.Body.Instructions[0]; 

    setMethodWriter.InsertBefore(
     firstExistingInstruction, 
     setMethodWriter.Create(OpCodes.Ldarg_0)); 

    setMethodWriter.InsertBefore(
     firstExistingInstruction, 
     setMethodWriter.Create(OpCodes.Ldfld, backingFieldReference)); 

    setMethodWriter.InsertBefore(
     firstExistingInstruction, 
     setMethodWriter.Create(OpCodes.Ldarg_1)); 

    setMethodWriter.InsertBefore(
     firstExistingInstruction, 
     setMethodWriter.Create(OpCodes.Call, objectEqualsMethodReference)); 

    setMethodWriter.InsertBefore(
     firstExistingInstruction, 
     setMethodWriter.Create(OpCodes.Brfalse_S, firstExistingInstruction)); 

    setMethodWriter.InsertBefore(
     firstExistingInstruction, 
     setMethodWriter.Create(OpCodes.Ret)); 

    assembly.Write(rewritten, new WriterParameters { WriteSymbols = true }); 

    Console.WriteLine("Done."); 
    Console.ReadKey(); 
} 

private static MethodReference GetSystemObjectEqualsMethodReference(
    AssemblyDefinition assembly 
) 
{ 

    var typeReference = assembly.MainModule.GetTypeReferences() 
     .Single(t => t.FullName == "System.Object"); 

    var typeDefinition = typeReference.Resolve(); 

    var methodDefinition = typeDefinition.Methods.Single(
          m => m.Name == "Equals" 
           && m.Parameters.Count == 2 
           && m.Parameters[0].ParameterType.Name == "Object" 
           && m.Parameters[1].ParameterType.Name == "Object" 
    ); 

    return methodDefinition; 
} 

private static FieldReference GetBackingFieldReference(
    TypeDefinition typeDefinition, 
    string propertyName 
) 
{ 
    var fieldName = "_" + propertyName; 
    var fieldReference = typeDefinition.Fields.Single(f => f.Name == fieldName); 

    return fieldReference; 
} 
} 
} 
+2

これは、あなたが記述されている問題を解決することはできませんが、それにもかかわらず、問題だ - あなたはldarg_0命令の1つを取る必要があります。 – cdhowie

+0

Reflectorで2つのldarg_0命令を見たことがあります:-)。ありがとう。 –

答えて

6

セシルは、するSystem.Reflectionとは異なり、基準と定義の区別を行い、それらのモジュールごとにスコープされています。つまり、自分の内部の別のモジュールからMethodDefinitionを単に使用することはできません。あなたはそれへの適切な参照を作成する必要があります。これは、Cecilの用語でimportingと呼ばれるプロセスです。

が交換:

var objectEqualsMethodReference = GetSystemObjectEqualsMethodReference(assembly); 

によって:

var objectEqualsMethodReference = assembly.MainModule.Import (GetSystemObjectEqualsMethodReference(assembly)); 

そして

具体的には、GetSystemObjectEqualsMethodReference corlibで定義されたメソッドを返します、あなたのモジュールで、それへの参照を作成する必要がありますILを固定することでそれが機能するはずです。私はそれをしていながら

また、方法:

private static MethodReference GetSystemObjectEqualsMethodReference(AssemblyDefinition assembly) 
{ 
    var typeReference = assembly.MainModule.GetTypeReferences() 
     .Single(t => t.FullName == "System.Object"); 

    var typeDefinition = typeReference.Resolve(); 

    var methodDefinition = typeDefinition.Methods.Single(
          m => m.Name == "Equals" 
           && m.Parameters.Count == 2 
           && m.Parameters[0].ParameterType.Name == "Object" 
           && m.Parameters[1].ParameterType.Name == "Object" 
    ); 

    return methodDefinition; 
} 

が良いように記述することになります。

private static MethodReference GetSystemObjectEqualsMethodReference(AssemblyDefinition assembly) 
{ 
    var @object = assembly.MainModule.TypeSystem.Object.Resolve(); 

    return @object.Methods.Single(
     m => m.Name == "Equals" 
      && m.Parameters.Count == 2 
      && m.Parameters[0].ParameterType.MetadataType == MetadataType.Object 
      && m.Parameters[1].ParameterType.MetadataType == MetadataType.Object); 
} 

そして

assembly.Write(rewritten, new WriterParameters { WriteSymbols = true }); 

はあまり意味がありませんアセンブリを読むときnew ReaderParameters { ReadSymbols = true }を渡さないと。

+0

パーフェクト。ありがとうございました。私は途中で輸入を試みたが、明らかに間違っていた。 –

1

KindOfMagic codeplexプロジェクトをご覧ください。

ほとんど同じですが、少し良くなります - Object.Equals()を呼び出すのではなく、ターゲットタイプで定義された等価演算子です。

http://kindofmagic.codeplex.com

関連する問題