2016-12-30 11 views
5

Chcę zbudować typ dynamicznie tak:Odbicie Emit dla obiektu Getter

public class Sample 
{ 
    Sample Parent { get; set; } 
    public Sample(Sample parent) 
    { 
     Parent = parent; 
    } 

    public int Depth 
    { 
     get 
     { 
      if (Parent == null) 
       return -1; 
      else 
       return Parent.Depth + 1; 
     } 
    } 
} 

kodu piszę to:

 const string assemblyName = "SampleAssembly"; 
     const string parentPproperty = "Parent"; 
     const string depthProperty = "Depth"; 
     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); 
     FieldBuilder parentField = typeBuilder.DefineField($"_{parentPproperty}", typeBuilder, FieldAttributes.Private); 
     PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(parentPproperty, PropertyAttributes.None, parentField.FieldType, Type.EmptyTypes); 
     MethodAttributes getSetAttr = MethodAttributes.Public | 
     MethodAttributes.SpecialName | MethodAttributes.HideBySig; 

     MethodBuilder getParentMethod = typeBuilder.DefineMethod($"get_{propertyBuilder.Name}", getSetAttr, parentField.FieldType, Type.EmptyTypes); 
     ILGenerator il = getParentMethod.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldfld, parentField); 
     il.Emit(OpCodes.Ret); 
     propertyBuilder.SetGetMethod(getParentMethod); 

     MethodBuilder setParentMethod = typeBuilder.DefineMethod($"set_{propertyBuilder.Name}", qetSetAttr, null, Type.EmptyTypes); 
     il = setParentMethod.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldarg_1); 
     il.Emit(OpCodes.Stfld, parentField); 
     il.Emit(OpCodes.Ret); 
     propertyBuilder.SetSetMethod(setParentMethod); 


     parentField = typeBuilder.DefineField($"_{depthProperty}", typeBuilder, FieldAttributes.Private); 
     propertyBuilder = typeBuilder.DefineProperty(depthProperty, PropertyAttributes.None, parentField.FieldType, Type.EmptyTypes); 
     MethodBuilder getDepthMethod = typeBuilder.DefineMethod($"get_{depthProperty}", getSetAttr , parentField.FieldType, Type.EmptyTypes); 
     il = getDepthMethod.GetILGenerator(); 
     LocalBuilder lb = il.DeclareLocal(typeof(bool)); 

     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Call, getParentMethod); 
     il.Emit(OpCodes.Ldnull); 
     il.Emit(OpCodes.Ceq); 
     il.Emit(OpCodes.Stloc_0); 
     il.Emit(OpCodes.Ldloc_0); 
     il.Emit(OpCodes.Brfalse_S); 
     il.Emit(OpCodes.Ldc_I4_1); 
     il.Emit(OpCodes.Stloc_1); 
     il.Emit(OpCodes.Br_S); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Call, getParentMethod); 
     il.Emit(OpCodes.Callvirt, getDepthMethod); 
     il.Emit(OpCodes.Ldc_I4_1); 
     il.Emit(OpCodes.Add); 
     il.Emit(OpCodes.Stloc_1); 
     il.Emit(OpCodes.Br_S); 
     il.Emit(OpCodes.Ldloc_1); 
     il.Emit(OpCodes.Ret); 
     propertyBuilder.SetGetMethod(getDepthMethod); 

     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, setParentMethod);   
     il.Emit(OpCodes.Ret); 

     Type type = typeBuilder.CreateType(); 
     var obj1 = Activator.CreateInstance(type, null); 

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

     assemblyBuilder.Save(assemblyFileName); 

Chyba miałem problemu w budowaniu konstruktora i głębokość metodę nieruchomość getter .

Proszę mi pomóc wydostać się z tego.

Instancja również nie jest tworzona.

Dzięki

Odpowiedz

3

W kodzie jest kilka problemów.

W swojej metodzie rodzic setter Przegapiłeś zadeklarować parametr:

MethodBuilder setParentMethod = typeBuilder.DefineMethod($"set_{propertyBuilder.Name}", getSetAttr, null, new [] { propertyBuilder.PropertyType }); 

Jesteś deklarowania redundantny pole oporowe, po prostu usunąć tę linię:

parentField = typeBuilder.DefineField($"_{depthProperty}", typeBuilder, FieldAttributes.Private); 

Twoja nieruchomość deepth jest z niewłaściwy typ, musi być typu int:

propertyBuilder = typeBuilder.DefineProperty(depthProperty, PropertyAttributes.None, typeof(int), Type.EmptyTypes); 
MethodBuilder getDepthMethod = typeBuilder.DefineMethod($"get_{depthProperty}", getSetAttr, propertyBuilder.PropertyType, Type.EmptyTypes); 

Kod IL, którym jesteś ge neratyzacja dla twojej wyliczonej właściwości wydaje się być debugowaniem kodu, zastąpiłem go kodem zwolnienia. Również emitują niekompletne rozgałęzienia instrukcji, należy podać etykietę docelowy jako drugi parametr, przyjrzeć się tej methodbody robocza:

il = getDepthMethod.GetILGenerator(); 

var notNullLabel = il.DefineLabel(); 

il.Emit(OpCodes.Ldarg_0); 
il.Emit(OpCodes.Call, getParentMethod); 
il.Emit(OpCodes.Brtrue_S, notNullLabel); 
il.Emit(OpCodes.Ldc_I4_M1); 
il.Emit(OpCodes.Ret); 
il.MarkLabel(notNullLabel); 
il.Emit(OpCodes.Ldarg_0); 
il.Emit(OpCodes.Call, getParentMethod); 
il.Emit(OpCodes.Callvirt, getDepthMethod); 
il.Emit(OpCodes.Ldc_I4_1); 
il.Emit(OpCodes.Add); 
il.Emit(OpCodes.Ret); 

Ostatni, ale nie najmniej, trzeba owinąć zerowego argumentu w tablicy obiektu tak Aktywator może znaleźć oczekiwane przeciążenie konstruktora:

var obj1 = Activator.CreateInstance(type, new object[] { null }); 
+0

Dziękuję, thehennyy, za uwagi i poprawki. To bardzo miłe z twojej strony. –

Powiązane problemy