2016-11-27 84 views
0

我在運行時使用反射創建類,基於ClassName和屬性列表,它們是實現INotifyPropertyChanged和OnPropertyChanged的父類「DataObject」的子類,但是當我試圖通過下面的方法來設置屬性,我得到一個「場令牌超出範圍」的異常:OnpropertyChanged在運行時添加屬性設置器

 private void dataGrid_AddingNewItem(object sender, AddingNewItemEventArgs e) 
    { 
     object obj = Activator.CreateInstance(currentType); 

     PropertyInfo[] properties = obj.GetType().GetProperties(); 
     try 
     { 
      foreach (PropertyInfo prop in properties) 
      { 
       if (prop.PropertyType == typeof(string) && prop.CanWrite) 
       { prop.SetValue(obj, "-", null); } 
       //else 
       //{ prop.SetValue(obj, 0, null); } 
      } 
     } 
     catch (Exception ex) 
     { 
      if (ex.InnerException != null) 
      { 
       throw ex.InnerException; 
      } 
     } 

     e.NewItem = obj; 
    } 

這就是我想每個屬性的工作(LastChange是從父類的靜態字符串):

public string Provaa { get { return provaa; } 
set { LastChange = ToString(); provaa = value; OnPropertyChanged("Provaa"); } } 

,這是這是怎麼翻譯成MSIL:

.method public hidebysig specialname instance void 
    set_Provaa(string 'value') cil managed 
{ 
// Code size  32 (0x20) 
.maxstack 8 
IL_0000: nop 
IL_0001: ldarg.0 
IL_0002: callvirt instance string [mscorlib]System.Object::ToString() 
IL_0007: stsfld  string EYBDataManager.DataObject::LastChange 
IL_000c: ldarg.0 
IL_000d: ldarg.1 
IL_000e: stfld  string EYBDataManager.Prova::provaa 
IL_0013: ldarg.0 
IL_0014: ldstr  "Provaa" 
IL_0019: call  instance void EYBDataManager.DataObject::OnPropertyChanged(string) 
IL_001e: nop 
IL_001f: ret 
} // end of method Prova::set_Provaa 

而且最後這是我正在試圖重新創建使用反射:

MethodBuilder currSetPropMthdBldr = typeBuilder.DefineMethod("set_value", GetSetAttr, null, new Type[] { prop.ActualType }); 
ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator(); 
currSetIL.Emit(OpCodes.Ldarg_0); 
currSetIL.Emit(OpCodes.Callvirt, typeof(Object).GetMethod("ToString")); 
currSetIL.Emit(OpCodes.Stsfld, typeof(DataObject).GetField("LastChange", BindingFlags.Static | BindingFlags.Public); 
currSetIL.Emit(OpCodes.Ldarg_0); 
currSetIL.Emit(OpCodes.Ldarg_1); 
currSetIL.Emit(OpCodes.Stfld, field); 
currSetIL.Emit(OpCodes.Ldarg_0); 
currSetIL.Emit(OpCodes.Ldstr, propertyName); 
currSetIL.Emit(OpCodes.Callvirt, typeof(DataObject).GetMethod("OnPropertyChanged", new Type[1] { typeof(string) })); 
currSetIL.Emit(OpCodes.Ret); 

這是「CreateClass」方法的一部分:

public static void CreateClass(string className, List<PropertyTemplate> properties) 
{ 
AssemblyName assemblyName = new AssemblyName(); 
assemblyName.Name = "tmpAssembly"; 
AssemblyBuilder assemblyBuilder = System.Threading.Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); 
ModuleBuilder module = assemblyBuilder.DefineDynamicModule("tmpModule"); 
TypeBuilder typeBuilder = module.DefineType(className, TypeAttributes.Public | TypeAttributes.Class, typeof(DataObject)); 

foreach (PropertyTemplate prop in properties) 
{ 
    string propertyName = prop.Name; 
    FieldBuilder field = typeBuilder.DefineField("p_" + propertyName, prop.ActualType, FieldAttributes.Private); 
    PropertyBuilder property = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, prop.ActualType, new Type[] { prop.ActualType }); 
    MethodAttributes GetSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; 

    MethodBuilder currGetPropMthdBldr = typeBuilder.DefineMethod("get_value", GetSetAttr, prop.ActualType, Type.EmptyTypes); 
    ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator(); 
    currGetIL.Emit(OpCodes.Ldarg_0); 
    currGetIL.Emit(OpCodes.Ldfld, field); 
    currGetIL.Emit(OpCodes.Ret); 


    ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator(); 
    currSetIL.Emit(OpCodes.Ldarg_0); 
    currSetIL.Emit(OpCodes.Callvirt, typeof(Object).GetMethod("ToString")); 
    currSetIL.Emit(OpCodes.Stsfld, DataObject.LastChange); 
    currSetIL.Emit(OpCodes.Ldarg_0); 
    currSetIL.Emit(OpCodes.Ldarg_1); 
    currSetIL.Emit(OpCodes.Stfld, field); 
    currSetIL.Emit(OpCodes.Ldarg_0); 
    currSetIL.Emit(OpCodes.Ldstr, propertyName); 
    currSetIL.Emit(OpCodes.Callvirt, typeof(DataObject).GetMethod("OnPropertyChanged", new Type[1] { typeof(string) })); 
    currSetIL.Emit(OpCodes.Ret); 

    property.SetGetMethod(currGetPropMthdBldr); 
    property.SetSetMethod(currSetPropMthdBldr); 
} 

Type genType = typeBuilder.CreateType(); 
if (Templates.ContainsKey(className)) 
    Templates[className] = genType; 
else 
    Templates.Add(className, genType); 
} 

我懷疑我需要在設置值時指定類的名稱和模塊名稱,但不知道如何,儘管Activator使用所有正確的屬性創建類實例,所以pr當建立SetMethod的時候,我可能會犯一些錯誤,有人能幫助我嗎?

編輯父類:

public class DataObject : INotifyPropertyChanged 
{ 
    public static string LastChange = ""; 

    public void OnPropertyChanged(string propertyName) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    public override string ToString() 
    { 
     string tostring = "|"; 
     PropertyInfo[] properties = this.GetType().GetProperties(); 
     foreach (PropertyInfo prop in properties) 
     { 
      tostring += " " + prop.GetValue(this, null) + " |"; 
     } 
     return tostring; 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

回答

1

我看到你的二傳手IL代碼

兩個錯誤此:

currSetIL.EmitCall(OpCodes.Call, typeof(Object).GetMethod("ToString"), new Type[0]); 

需:

currSetIL.Emit(OpCodes.Callvirt, typeof(Object).GetMethod("ToString"), new Type[0]); 

這:

currSetIL.Emit(OpCodes.Ldstr, propertyName); 
currSetIL.Emit(OpCodes.Call, typeof(DataObject).GetMethod("OnPropertyChanged", new Type[1] { typeof(string) })); 

需:

currSetIL.Emit(OpCodes.ldarg_0); 
currSetIL.Emit(OpCodes.Ldstr, propertyName); 
currSetIL.Emit(OpCodes.Callvirt, typeof(DataObject).GetMethod("OnPropertyChanged", new Type[1] { typeof(string) })); 

我在你把callvirt在評論第一種情況下看到的和你call爲什麼取代它呢?它是一個虛擬方法的調用。

另外在第二種情況下,它的虛擬呼叫和您忘記加載this首先調用實例方法OnPropertyChanged

這是我現在看到的,讓我知道它是否修復,如果不是,我會嘗試在我自己中重現它。

更新

替換此:

currSetIL.Emit(OpCodes.Stsfld, DataObject.LastChange) 

有了這個:

currSetIL.Emit(OpCodes.Stsfld, typeof(DataObject).GetField("LastChange", BindingFlags.Static | BindingFlags.Public) 
+0

你好,謝謝你的回答,我修改了代碼之後你的建議,但我仍然嘗試設置值時,出現「字段標記超出範圍」異常,我也添加了父類,如果它可能是有用的 –

+0

@EatYourB eetS吸氣劑的作用?如果是,請嘗試在不設置靜態字段的情況下發射setter。我直到明天都不能檢查它。讓我知道它的工作。 –

+0

是的,現在它沒有這3行 'currSetIL.Emit(OpCodes.Ldarg_0); (OpCodes.Call,typeof(DataObject).GetMethod(「ToString」,new Type [0])); currSetIL.Emit(OpCodes.Stsfld,DataObject.LastChange);' 我猜的第三個特別的問題 –

相關問題