2017-01-05 4 views
0

У меня проблема с генерацией getterMethod для HasChildren Property в следующем коде.Reflection Emit: Как сгенерировать getter для сравнения

Может ли кто-нибудь помочь мне в этом?

код в 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

Посмотрите на msdn для ILGenerator, он предоставляет все, что вам нужно, метки для правильного разветвления и определения локальных переменных. Для быстрой и простой генерации простых блоков IL я могу порекомендовать tryroslyn.azurewebsites.net. – thehennyy

ответ

0

Вы не указываете общие аргументы в методе Count.

Вы должны указать, что общий аргумент является Sample с помощью MakeGenericMethod

Вы также дополнительный Ldarg_0 сразу после notNullLabel. this уже на стеке в данный момент, благодаря операции dup в самом начале.

Вот слегка измененная версия работает которым вы ожидаете:

 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

Спасибо, теневый и Регис-порталес. –

Смежные вопросы