我正在尝试实现一个可以绑定到Avalonia DataGrid的动态类。为了使用DataGrid版本,这个类应该实现INtifyProperty。经过搜索,似乎最好的选择是使用System。反思。发送包裹。
我能够在动态类和该类的实例中生成动态属性。此外,我可以创建一个ObservableCollection并将其绑定到DataGrid,用二元类的实例填充它,数据就会在DataGrid中正确显示。
我的问题是INNotifyProperty的实现。实际上,当我从动态属性的set方法调用OnPropertyChanged方法时,我遇到了一个错误。
这是我的动态课堂工厂:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
namespace MyApp.App.Reflection
{
public class DynamicClassFactory
{
AssemblyName _assemblyName;
public DynamicClassFactory(string className)
{
_assemblyName = new AssemblyName(className);
}
public dynamic? CreateObject(string[] propertyNames, Type[] types)
{
if (propertyNames.Length != types.Length)
{
throw new ArgumentException("The number of property names should match their corresponding types number");
}
TypeBuilder dynamicClass = CreateClass();
CreateConstructor(dynamicClass);
var onPropertyChangedMethod = ImplementINotifyPropertyChanged(dynamicClass);
for (int i = 0; i < propertyNames.Length; i++)
{
CreateProperties(dynamicClass, propertyNames[i], types[i], onPropertyChangedMethod);
}
Type type = dynamicClass.CreateType();
return Activator.CreateInstance(type);
}
private TypeBuilder CreateClass()
{
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(_assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");
TypeBuilder typeBuilder = moduleBuilder.DefineType(_assemblyName.FullName,
TypeAttributes.Public
| TypeAttributes.Class
| TypeAttributes.AutoClass
| TypeAttributes.AnsiClass
| TypeAttributes.BeforeFieldInit
| TypeAttributes.AutoLayout, null);
typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanged));
return typeBuilder;
}
private void CreateConstructor(TypeBuilder typeBuilder)
{
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
}
private MethodBuilder ImplementINotifyPropertyChanged(TypeBuilder typeBuilder)
{
var evtField = typeBuilder.DefineField("PropertyChanged", typeof(PropertyChangedEventHandler), FieldAttributes.Private);
var evtBuilder = typeBuilder.DefineEvent("PropertyChanged", EventAttributes.None, typeof(PropertyChangedEventHandler));
var addMethod = typeBuilder.DefineMethod("add_PropertyChanged",
MethodAttributes.Public | MethodAttributes.Virtual,
typeof(void),
new Type[] { typeof(PropertyChangedEventHandler) });
var addIl = addMethod.GetILGenerator();
addIl.Emit(OpCodes.Ldarg_0);
addIl.Emit(OpCodes.Ldarg_0);
addIl.Emit(OpCodes.Ldfld, evtField);
addIl.Emit(OpCodes.Ldarg_1);
addIl.Emit(OpCodes.Call,
typeof(Delegate).GetMethod("Combine", new Type[] { typeof(Delegate), typeof(Delegate) }));
addIl.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler));
addIl.Emit(OpCodes.Stfld, evtField);
addIl.Emit(OpCodes.Ret);
var removeMethod = typeBuilder.DefineMethod("remove_PropertyChanged",
MethodAttributes.Public | MethodAttributes.Virtual,
typeof(void),
new Type[] { typeof(PropertyChangedEventHandler) });
var removeIl = removeMethod.GetILGenerator();
removeIl.Emit(OpCodes.Ldarg_0);
removeIl.Emit(OpCodes.Ldarg_0);
removeIl.Emit(OpCodes.Ldfld, evtField);
removeIl.Emit(OpCodes.Ldarg_1);
removeIl.Emit(OpCodes.Call,
typeof(Delegate).GetMethod("Remove", new Type[] { typeof(Delegate), typeof(Delegate) }));
removeIl.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler));
removeIl.Emit(OpCodes.Stfld, evtField);
removeIl.Emit(OpCodes.Ret);
evtBuilder.SetAddOnMethod(addMethod);
evtBuilder.SetRemoveOnMethod(removeMethod);
var onPropertyChangedMethod = typeBuilder.DefineMethod("OnPropertyChanged",
MethodAttributes.Family | MethodAttributes.Virtual,
typeof(void),
new Type[] { typeof(string) });
var onPropertyChangedIl = onPropertyChangedMethod.GetILGenerator();
var retLabel = onPropertyChangedIl.DefineLabel();
onPropertyChangedIl.Emit(OpCodes.Ldarg_0);
onPropertyChangedIl.Emit(OpCodes.Ldfld, evtField);
onPropertyChangedIl.Emit(OpCodes.Dup);
onPropertyChangedIl.Emit(OpCodes.Brfalse_S, retLabel);
onPropertyChangedIl.Emit(OpCodes.Ldarg_0);
onPropertyChangedIl.Emit(OpCodes.Ldarg_1);
onPropertyChangedIl.Emit(OpCodes.Newobj, typeof(PropertyChangedEventArgs).GetConstructor(new Type[] { typeof(string) }));
onPropertyChangedIl.Emit(OpCodes.Callvirt, typeof(PropertyChangedEventHandler).GetMethod("Invoke", new Type[] { typeof(object), typeof(PropertyChangedEventArgs) }));
onPropertyChangedIl.MarkLabel(retLabel);
onPropertyChangedIl.Emit(OpCodes.Ret);
return onPropertyChangedMethod;
}
private void CreateProperties(TypeBuilder typeBuilder, string propertyName, Type type, MethodBuilder onPropertyChangedMethod)
{
FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, type, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, type, null);
MethodBuilder getPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, type, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new Type[] { type });
ILGenerator setIl = setPropMthdBldr.GetILGenerator();
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldstr, propertyName);
setIl.Emit(OpCodes.Call, onPropertyChangedMethod);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}
}
这是创建实例的视图模型类:
using MyApp.App.Reflection;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyApp.App.Components
{
public class DataViewerControlViewModel
{
public ObservableCollection<dynamic> Data { get; } = new ObservableCollection<dynamic>();
public DataViewerControlViewModel()
{
DynamicClassFactory dynamicClassFactory = new DynamicClassFactory("DynamicRecordsClass");
var dynamicClass1 = dynamicClassFactory.CreateObject(new string[] { "ID", "Name", "Age", "IsAdmin" }, new Type[] { typeof(int), typeof(string), typeof(int), typeof(bool) });
dynamicClass1.ID = 1;
dynamicClass1.Name = "John Doe";
dynamicClass1.Age = 30;
dynamicClass1.IsAdmin = true;
Data.Add(dynamicClass1);
var dynamicClass2 = dynamicClassFactory.CreateObject(new string[] { "ID", "Name", "Age", "IsAdmin" }, new Type[] { typeof(int), typeof(string), typeof(int), typeof(bool) });
dynamicClass2.ID = 2;
dynamicClass2.Name = "Jane Doe";
dynamicClass2.Age = 25;
dynamicClass2.IsAdmin = false;
Data.Add(dynamicClass2);
Data.CollectionChanged += Data_CollectionChanged;
}
private void Data_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace)
{
}
}
}
}
事实上,我被困在这一点上:
//....
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldstr, propertyName);
setIl.Emit(OpCodes.Call, onPropertyChangedMethod);
//....
程序编译时没有任何错误。但在运行时,它会产生一个错误:
System.InvalidProgramException: 'Common Language Runtime detected an invalid program.'
,在第一次分配时:
dynamicClass1.ID = 1;
。如果我注释掉这些行,程序将按预期运行,显然没有属性更改通知。
问题在哪里?我定义了错误的INtifyProperty实现?我在set方法中定义了对notify事件的错误调用?