2017-01-05 2 views
0

다음 코드에서 HasChildren 속성에 대한 getterMethod 생성시 문제가 있습니다.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

ILGenerator의 msdn을 살펴보면 로컬 변수의 올바른 분기 및 정의를위한 레이블, 필요한 레이블을 제공합니다. 간단한 일리노이 청크의 빠르고 쉬운 생성을 위해 나는 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에 감사드립니다. –