WPF c# 使用Emit自动创建通知类

参考
概念参考自 https://www.codewrecks.com/post/old/2008/08/implement-inotifypropertychanged-with-dynamic-code-generation/
和DevExpress的MVVM框架 Runtime-generated POCO View Models
代码实现来自ChatGPT 抽卡

原始类

public class Class1
{
public virtual int MyProperty { get; set; }
}

希望调用一行代码后自动帮我创建的派生类

public class Class2 : Class1, INotifyPropertyChanged
{
public override int MyProperty
{
get { return base.MyProperty; }
set
{
base.MyProperty = value;
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("MyProperty"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}

Emit动态创建实现

注意: .netcore 没有保存Emit程序集和AppDomain的实现

/// <summary>
/// 根据原始类自动创建通知类
/// </summary>
public static class NotifyTypeEmit
{
public static T CreateInstance<T>()
{
return (T)CreateInstance(typeof(T));
}
public static object CreateInstance(Type baseType)
{
var type = CreateType(baseType);
return Activator.CreateInstance(type);
}
/// <summary>
///根据基础类创建派生类,自带缓存
/// </summary>
/// <param name="baseType"></param>
/// <returns></returns>
public static Type CreateType(Type baseType)
{
if (!s_extensionTypes.ContainsKey(baseType))
{
var type = _CreateType(baseType);
s_extensionTypes.TryAdd(baseType, type);
}
return s_extensionTypes[baseType];
}
private static ConcurrentDictionary<Type, Type> s_extensionTypes
= new ConcurrentDictionary<Type, Type>();
private static Type _CreateType(Type baseType)
{
var assemblyName = new AssemblyName("TrykleMvvmToolkit.NotifyTypeEmit.Assembly");
//var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
// .net 4.0 可以用这个代替
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("TrykleMvvmToolkit.NotifyTypeEmit.Module", "NortiyTypeEmit_Test.dll");
var typeBuilder = moduleBuilder.DefineType($"{baseType.Name}_EXTENSION", TypeAttributes.Public | TypeAttributes.Class, baseType, new[] { typeof(INotifyPropertyChanged) });
// 实现 INotifyPropertyChanged 接口
var eventBuilder = typeBuilder.DefineEvent("PropertyChanged", EventAttributes.None, typeof(PropertyChangedEventHandler));
var eventFieldBuilder = typeBuilder.DefineField("PropertyChanged", typeof(PropertyChangedEventHandler), FieldAttributes.Private);
//
var interfaceMethodInfo = typeof(INotifyPropertyChanged).GetMethod("add_PropertyChanged");
var addMethod = typeBuilder.DefineMethod(interfaceMethodInfo.Name, MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual, typeof(void), new Type[] { typeof(PropertyChangedEventHandler) });
var ilAddMethod = addMethod.GetILGenerator();
ilAddMethod.Emit(OpCodes.Ldarg_0);
ilAddMethod.Emit(OpCodes.Ldarg_0);
ilAddMethod.Emit(OpCodes.Ldfld, eventFieldBuilder);
ilAddMethod.Emit(OpCodes.Ldarg_1);
ilAddMethod.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Combine", new Type[] { typeof(Delegate), typeof(Delegate) }));
ilAddMethod.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler));
ilAddMethod.Emit(OpCodes.Stfld, eventFieldBuilder);
ilAddMethod.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(addMethod, interfaceMethodInfo);
eventBuilder.SetAddOnMethod(addMethod);
//
interfaceMethodInfo = typeof(INotifyPropertyChanged).GetMethod("remove_PropertyChanged");
var removeMethod = typeBuilder.DefineMethod(interfaceMethodInfo.Name, MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual, typeof(void), new Type[] { typeof(PropertyChangedEventHandler) });
var ilRemoveMethod = removeMethod.GetILGenerator();
ilRemoveMethod.Emit(OpCodes.Ldarg_0);
ilRemoveMethod.Emit(OpCodes.Ldarg_0);
ilRemoveMethod.Emit(OpCodes.Ldfld, eventFieldBuilder);
ilRemoveMethod.Emit(OpCodes.Ldarg_1);
ilRemoveMethod.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Remove", new Type[] { typeof(Delegate), typeof(Delegate) }));
ilRemoveMethod.Emit(OpCodes.Castclass, typeof(PropertyChangedEventHandler));
ilRemoveMethod.Emit(OpCodes.Stfld, eventFieldBuilder);
ilRemoveMethod.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(removeMethod, interfaceMethodInfo);
eventBuilder.SetRemoveOnMethod(removeMethod);
foreach (var propertyInfo in baseType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (propertyInfo.GetGetMethod() == null || (propertyInfo.GetSetMethod() == null))
{
continue;
}
var propertyName = propertyInfo.Name;
var getPropertyName = $"get_{propertyName}";
var setPropertyName = $"set_{propertyName}";
var propertyType = propertyInfo.PropertyType;
var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, null);
var methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Virtual;
var getterMethodBuilder = typeBuilder.DefineMethod(getPropertyName, methodAttributes, propertyType, Type.EmptyTypes);
var getterIL = getterMethodBuilder.GetILGenerator();
getterIL.Emit(OpCodes.Ldarg_0);
getterIL.Emit(OpCodes.Call, baseType.GetProperty(propertyName).GetGetMethod());
getterIL.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getterMethodBuilder);
var setterMethodBuilder = typeBuilder.DefineMethod(setPropertyName, methodAttributes, null, new[] { propertyType });
var setterIL = setterMethodBuilder.GetILGenerator();
setterIL.Emit(OpCodes.Ldarg_0);
setterIL.Emit(OpCodes.Ldarg_1);
setterIL.Emit(OpCodes.Call, baseType.GetProperty(propertyName).GetSetMethod());
setterIL.Emit(OpCodes.Ldarg_0);
setterIL.Emit(OpCodes.Ldfld, eventFieldBuilder);
var label = setterIL.DefineLabel();
setterIL.Emit(OpCodes.Brfalse_S, label);
setterIL.Emit(OpCodes.Ldarg_0);
setterIL.Emit(OpCodes.Ldfld, eventFieldBuilder);
setterIL.Emit(OpCodes.Ldarg_0);
setterIL.Emit(OpCodes.Ldstr, propertyName);
setterIL.Emit(OpCodes.Newobj, typeof(PropertyChangedEventArgs).GetConstructor(new[] { typeof(string) })
);
setterIL.Emit(OpCodes.Callvirt, typeof(PropertyChangedEventHandler).GetMethod("Invoke"));
setterIL.MarkLabel(label);
setterIL.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setterMethodBuilder);
}
//end
typeBuilder.CreateType();
assemblyBuilder.Save("NortiyTypeEmit_Test.dll");
return typeBuilder;
}
}

测试

static void Main(string[] args)
{
var cls1 = NotifyTypeEmit.CreateInstance<Class1>();
(cls1 as INotifyPropertyChanged).PropertyChanged += (sender, prop) =>
{
Console.WriteLine($"Property changed: {prop.PropertyName}");
};
cls1.MyProperty = 2;
Console.Read();
}
posted @   trykle  阅读(72)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示