2017-01-05 8 views
0

次のコードでHasChildrenプロパティのgetterMethodを生成する際に問題があります。Reflection Emit:比較のためにゲッターを生成する方法

誰かが私を助けてくれますか?

コードは、C#で動作していない

public class Sample 
{  
    public ObservableCollection<Sample> Children { get; set; }  
    public bool HasChildren { get { return Children?.Count() > 0; } }   
} 

コードがmethodbuilderのためにここに引用されています。

    const string assemblyName = "HasChildrenAssembly"; 
     const string childrenProperty = "Children"; 
     const string hasChildrenProperty = "HasChildren"; 
     const string typeName = "Sample"; 
     const string assemblyFileName = assemblyName + ".dll"; 

     AppDomain domain = AppDomain.CurrentDomain; 
     AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave); 
     ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFileName); 
     TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public); 

     Type typeOfChildren = typeof(ObservableCollection<>); 
     Type genericTypeOfChildren = typeOfChildren.MakeGenericType(typeBuilder); 

     FieldBuilder childrenField = typeBuilder.DefineField($"_{childrenProperty}", genericTypeOfChildren, FieldAttributes.Private); 
     PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(childrenProperty, PropertyAttributes.None, childrenField.FieldType, Type.EmptyTypes); 

     MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; 
     MethodBuilder getChildrenMethod = typeBuilder.DefineMethod($"get_{propertyBuilder.Name}", getSetAttr, childrenField.FieldType, Type.EmptyTypes); 
     ILGenerator il = getChildrenMethod.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldfld, childrenField); 
     il.Emit(OpCodes.Ret); 
     propertyBuilder.SetGetMethod(getChildrenMethod); 

     MethodBuilder setChildrenMethod = typeBuilder.DefineMethod($"set_{propertyBuilder.Name}", getSetAttr, null, new[] { propertyBuilder.PropertyType });   
     il = setChildrenMethod.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldarg_1); 
     il.Emit(OpCodes.Stfld, childrenField); 
     il.Emit(OpCodes.Ret); 
     propertyBuilder.SetSetMethod(setChildrenMethod); 

     MethodInfo countMethodInfo = typeof(System.Linq.Enumerable).GetMethods().Single(method => method.Name == "Count" && method.IsStatic && method.GetParameters().Length == 1); 
     propertyBuilder = typeBuilder.DefineProperty(hasChildrenProperty, PropertyAttributes.None, typeof(bool), Type.EmptyTypes); 
     MethodBuilder getHasChildrenMethod = typeBuilder.DefineMethod($"get_{hasChildrenProperty}", getSetAttr, propertyBuilder.PropertyType, Type.EmptyTypes); 
     il = getHasChildrenMethod.GetILGenerator(); 
     var notNullLabel = il.DefineLabel(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Call, getChildrenMethod); 
     il.Emit(OpCodes.Dup); 
     il.Emit(OpCodes.Brtrue_S, notNullLabel);   
     il.Emit(OpCodes.Ldc_I4_0); 
     il.Emit(OpCodes.Ret); 
     il.MarkLabel(notNullLabel); 

     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Call, countMethodInfo); 
     il.Emit(OpCodes.Ldc_I4_0); 
     il.Emit(OpCodes.Cgt); 
     il.Emit(OpCodes.Ret); 
     propertyBuilder.SetGetMethod(getHasChildrenMethod); 

     ConstructorBuilder constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] { typeBuilder }); 
     il = constructor.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldarg_1); 
     il.Emit(OpCodes.Call, setChildrenMethod); 
     il.Emit(OpCodes.Ret); 

     Type type = typeBuilder.CreateType(); 
     var obj1 = Activator.CreateInstance(type, new object[] { null }); 

     var obj2 = Activator.CreateInstance(type, obj1); 

     assemblyBuilder.Save(assemblyFileName); 

ありがとうございます。

+0

あなた自身によってILコードを書いてみるべきではない、ハイレベルのコンパイラはあなたのためにこれをやらせる:ここでは

は、わずかに修正されたバージョンのウィッヒが期待どおりに動作しています。このILコードシーケンスが正しく出力された場合に実行されるかどうかはわかりませんが、少なくともあなたが使用しているローカル変数を宣言しておらず、分岐命令も不完全です。 – thehennyy

+0

ご意見ありがとうございます。私が参照できるものはありますか? –

+0

ILGeneratorのmsdnを見ると、ローカル変数の正しい分岐と定義のためのラベル、必要なものすべてが提供されます。シンプルなILのチャンクを素早く簡単に生成するために、私はtryroslyn.azurewebsites.netをお勧めします。 – thehennyy

答えて

0

Countメソッドで汎用引数を指定しません。

あなたは、一般的な引数はあなたもnotNullLabel後、余分なLdarg_0権利を持っているMakeGenericMethod

を使用してSampleであることを指定する必要があります。非常に最初のdup操作のおかげで、thisはすでにこの時点でスタックに入っています。

 AppDomain ad = AppDomain.CurrentDomain; 
     AssemblyBuilder ab = ad.DefineDynamicAssembly(new AssemblyName("toto.dll"), AssemblyBuilderAccess.RunAndSave); 
     ModuleBuilder mb = ab.DefineDynamicModule("toto.dll"); 
     TypeBuilder tb = mb.DefineType("toto.Sample", TypeAttributes.Public | TypeAttributes.Class); 


     FieldBuilder fb = tb.DefineField("<Children>k__BackingField", typeof(ObservableCollection<>).MakeGenericType(tb), FieldAttributes.Private | FieldAttributes.InitOnly); 
     fb.SetCustomAttribute(new CustomAttributeBuilder(typeof(CompilerGeneratedAttribute).GetConstructor(new Type[0]), new object[0])); 

     PropertyBuilder pb = tb.DefineProperty("Children", PropertyAttributes.None, typeof(ObservableCollection<>).MakeGenericType(tb), new Type[0]); 

     MethodBuilder getter = tb.DefineMethod("get_Children", MethodAttributes.Public, CallingConventions.HasThis, typeof(ObservableCollection<>).MakeGenericType(tb), new Type[0]); 
     getter.SetCustomAttribute(new CustomAttributeBuilder(typeof(CompilerGeneratedAttribute).GetConstructor(new Type[0]), new object[0])); 
     ILGenerator ilgen = getter.GetILGenerator(); 
     ilgen.Emit(OpCodes.Ldarg_0); 
     ilgen.Emit(OpCodes.Ldfld, fb); 
     ilgen.Emit(OpCodes.Ret); 

     pb.SetGetMethod(getter); 

     PropertyBuilder pbhas = tb.DefineProperty("HasChildren", PropertyAttributes.None, typeof(bool), new Type[0]); 
     MethodBuilder hasgetter = tb.DefineMethod("get_HasChildren", MethodAttributes.Public, CallingConventions.HasThis, typeof(bool), new Type[0]); 
     ilgen = hasgetter.GetILGenerator(); 
     Label notNullLabel = ilgen.DefineLabel(); 

     ilgen.Emit(OpCodes.Ldarg_0); 
     ilgen.Emit(OpCodes.Call, getter); 
     ilgen.Emit(OpCodes.Dup); 
     ilgen.Emit(OpCodes.Brtrue, notNullLabel); 

     ilgen.Emit(OpCodes.Pop); 
     ilgen.Emit(OpCodes.Ldc_I4_0); 
     ilgen.Emit(OpCodes.Ret); 

     ilgen.MarkLabel(notNullLabel); 


     MethodInfo mi = typeof(Enumerable).GetMethods() 
           .Where(m => m.Name == "Count" && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 1) 
           .First() 
           .MakeGenericMethod(tb); 

     ilgen.Emit(OpCodes.Call, mi); 
     ilgen.Emit(OpCodes.Ldc_I4_0); 
     ilgen.Emit(OpCodes.Cgt); 

     ilgen.Emit(OpCodes.Ret); 

     pbhas.SetGetMethod(hasgetter); 

     tb.CreateType(); 
     ab.Save("toto.dll"); 
+0

最後に無条件で分岐する必要はありません。代わりにret命令を出すことができます。また、問題を引き起こした元のコードには余分なldarg.0命令があることに気付くことができました。確かに – thehennyy

+0

。あなたの提案を統合しました –

+0

thehennyyとRegis-portalezありがとうございました。 –

関連する問題