Files
serein-flow/Library/Utils/DynamicObjectHelper.cs
fengjiayi 152077e9b5 1. 重新设计了Generate项目及相关特性的命名,避免与其他类型混淆。
2. 补充了部分注释。
3. 修改了删除容器节点时,容器内子节点未正确删除的问题。
2025-07-30 21:15:07 +08:00

598 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace Serein.Library.Utils
{
/// <summary>
/// 通过字典构造动态对象
/// </summary>
public class DynamicObjectHelper
{
// 类型缓存,键为类型的唯一名称(可以根据实际需求调整生成方式)
private static Dictionary<string, Type> typeCache = new Dictionary<string, Type>();
private static readonly AssemblyBuilder AssemblyBuilder;
private static readonly ModuleBuilder ModuleBuilder;
static DynamicObjectHelper() // 静态构造函数
{
var assemblyName = new AssemblyName("DynamicAssembly");
AssemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder = AssemblyBuilder.DefineDynamicModule("MainModule");
}
/// <summary>
/// 获取运行时创建过的类型
/// </summary>
/// <param name="className"></param>
/// <returns></returns>
public static Type GetCacheType(string className)
{
if(typeCache.TryGetValue(className, out var type))
{
return type;
}
return null;
}
/// <summary>
/// 解析字典属性并创建对象实例
/// </summary>
/// <param name="properties"></param>
/// <param name="typeName"></param>
/// <returns></returns>
public static object Resolve(IDictionary<string, object> properties, string typeName)
{
var obj = CreateObjectWithProperties(properties, typeName);
//SetPropertyValues(obj, properties);
return obj;
}
/// <summary>
/// 尝试解析字典属性并创建对象实例
/// </summary>
/// <param name="properties"></param>
/// <param name="typeName"></param>
/// <param name="result"></param>
/// <returns></returns>
public static bool TryResolve(IDictionary<string, object> properties, string typeName, out object result)
{
result = CreateObjectWithProperties(properties, typeName);
bool success = SetPropertyValuesWithValidation(result, properties);
return success;
}
/// <summary>
/// 打印对象属性及类型
/// </summary>
/// <param name="obj"></param>
/// <param name="indent"></param>
public static void PrintObjectProperties(object obj, string indent = "")
{
var objType = obj.GetType();
foreach (var prop in objType.GetProperties())
{
var value = prop.GetValue(obj);
SereinEnv.WriteLine(InfoType.INFO, $"{indent}{prop.Name} (Type: {prop.PropertyType.Name}): {value}");
if (value != null)
{
if (prop.PropertyType.IsArray) // 处理数组类型
{
var array = (Array)value;
SereinEnv.WriteLine(InfoType.INFO, $"{indent}{prop.Name} is an array with {array.Length} elements:");
for (int i = 0; i < array.Length; i++)
{
var element = array.GetValue(i);
if (element != null && element.GetType().IsClass && !(element is string))
{
SereinEnv.WriteLine(InfoType.INFO, $"{indent}\tArray[{i}] (Type: {element.GetType().Name}) contains a nested object:");
PrintObjectProperties(element, indent + "\t\t");
}
else
{
SereinEnv.WriteLine(InfoType.INFO, $"{indent}\tArray[{i}] (Type: {element?.GetType().Name}): {element}");
}
}
}
else if (value.GetType().IsClass && !(value is string)) // 处理嵌套对象
{
SereinEnv.WriteLine(InfoType.INFO, $"{indent}{prop.Name} contains a nested object:");
PrintObjectProperties(value, indent + "\t");
}
}
}
}
/// <summary>
/// 创建具有属性的类型
/// </summary>
/// <param name="properties">成员属性名称及类型</param>
/// <param name="typeName">类型名称</param>
/// <param name="isOverlay">是否覆盖</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static Type CreateTypeWithProperties(IDictionary<string, Type> properties, string typeName, bool isOverlay = false)
{
// 如果类型已经缓存,且没有显示指定需要覆盖,直接返回缓存的类型
if (typeCache.ContainsKey(typeName) && !isOverlay)
{
return typeCache[typeName];
}
// 定义动态程序集和模块
/*var assemblyName = new AssemblyName("DynamicAssembly");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = AssemblyBuilder.DefineDynamicModule("MainModule");
*/
// 定义动态类型
var typeBuilder = ModuleBuilder.DefineType(typeName, TypeAttributes.Public);
// 为每个属性名和值添加相应的属性到动态类型中
foreach (var kvp in properties)
{
string propName = kvp.Key;
object propValue = kvp.Value;
Type propType;
if (propValue is IList<Dictionary<string, Type>>) // 处理数组类型
{
var nestedPropValue = (propValue as IList<Dictionary<string, Type>>)[0];
var nestedType = CreateTypeWithProperties(nestedPropValue, $"{propName}Element");
propType = nestedType.GetType().MakeArrayType(); // 创建数组类型
}
else if (propValue is Dictionary<string, Type> nestedProperties)
{
// 如果值是嵌套的字典,递归创建嵌套类型
propType = CreateTypeWithProperties(nestedProperties, $"{typeName}_{propName}").GetType();
}
else if (propValue is Type type)
{
// 如果是普通类型,使用值的类型
propType = type ?? typeof(object);
}
else
{
throw new Exception($"无法解析的类型:{propValue}");
}
// 定义私有字段和公共属性
var fieldBuilder = typeBuilder.DefineField("_" + propName, propType, FieldAttributes.Private);
var propertyBuilder = typeBuilder.DefineProperty(propName, PropertyAttributes.HasDefault, propType, null);
// 定义 getter 方法
var getMethodBuilder = typeBuilder.DefineMethod(
"get_" + propName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
propType,
Type.EmptyTypes);
var getIL = getMethodBuilder.GetILGenerator();
getIL.Emit(OpCodes.Ldarg_0);
getIL.Emit(OpCodes.Ldfld, fieldBuilder);
getIL.Emit(OpCodes.Ret);
// 定义 setter 方法
var setMethodBuilder = typeBuilder.DefineMethod(
"set_" + propName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
null,
new Type[] { propType });
var setIL = setMethodBuilder.GetILGenerator();
setIL.Emit(OpCodes.Ldarg_0);
setIL.Emit(OpCodes.Ldarg_1);
setIL.Emit(OpCodes.Stfld, fieldBuilder);
setIL.Emit(OpCodes.Ret);
// 将 getter 和 setter 方法添加到属性
propertyBuilder.SetGetMethod(getMethodBuilder);
propertyBuilder.SetSetMethod(setMethodBuilder);
}
// 创建类型并缓存
var dynamicType = typeBuilder.CreateType();
typeCache[typeName] = dynamicType;
// 创建对象实例
return dynamicType;
}
/// <summary>
/// 创建继承通知接口、具有属性的类型
/// </summary>
/// <param name="properties"></param>
/// <param name="typeName"></param>
/// <param name="isOverlay"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static Type CreateTypeWithINotifyPropertyChanged(IDictionary<string, Type> properties, string typeName, bool isOverlay = false)
{
if (typeCache.ContainsKey(typeName) && !isOverlay)
return typeCache[typeName];
var typeBuilder = ModuleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class);
typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanged));
// 添加 PropertyChanged 字段
var eventHandlerType = typeof(PropertyChangedEventHandler);
var propertyChangedField = typeBuilder.DefineField("PropertyChanged", eventHandlerType, FieldAttributes.Private);
// 添加 PropertyChanged 事件
var eventBuilder = typeBuilder.DefineEvent("PropertyChanged", EventAttributes.None, eventHandlerType);
// add_PropertyChanged
var addMethod = typeBuilder.DefineMethod("add_PropertyChanged", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual | MethodAttributes.HideBySig, null, new[] { eventHandlerType });
{
var il = addMethod.GetILGenerator();
var combine = typeof(Delegate).GetMethod("Combine", new[] { typeof(Delegate), typeof(Delegate) });
// this.PropertyChanged = (PropertyChangedEventHandler)Delegate.Combine(this.PropertyChanged, value);
il.Emit(OpCodes.Ldarg_0); // 加载 this当前实例到栈
il.Emit(OpCodes.Ldarg_0); // 再次加载 this用于访问字段
il.Emit(OpCodes.Ldfld, propertyChangedField); // 加载 this.PropertyChanged 字段值
il.Emit(OpCodes.Ldarg_1); // 加载方法参数 value订阅者委托
il.Emit(OpCodes.Call, combine); // 调用 Delegate.Combine(a, b)
il.Emit(OpCodes.Castclass, eventHandlerType); // 强转回 PropertyChangedEventHandler 类型
il.Emit(OpCodes.Stfld, propertyChangedField); // 赋值给 this.PropertyChanged
il.Emit(OpCodes.Ret); // return
}
// remove_PropertyChanged
var removeMethod = typeBuilder.DefineMethod("remove_PropertyChanged", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual | MethodAttributes.HideBySig, null, new[] { eventHandlerType });
{
var il = removeMethod.GetILGenerator();
var remove = typeof(Delegate).GetMethod("Remove", new[] { typeof(Delegate), typeof(Delegate) });
// this.PropertyChanged = (PropertyChangedEventHandler)Delegate.Remove(this.PropertyChanged, value);
il.Emit(OpCodes.Ldarg_0); // 加载 this
il.Emit(OpCodes.Ldarg_0); // 再次加载 this
il.Emit(OpCodes.Ldfld, propertyChangedField); // 加载字段 this.PropertyChanged
il.Emit(OpCodes.Ldarg_1); // 加载 value要移除的委托
il.Emit(OpCodes.Call, remove); // 调用 Delegate.Remove(a, b)
il.Emit(OpCodes.Castclass, eventHandlerType); // 转换为 PropertyChangedEventHandler
il.Emit(OpCodes.Stfld, propertyChangedField); // 设置 this.PropertyChanged = 结果
il.Emit(OpCodes.Ret); // return
}
eventBuilder.SetAddOnMethod(addMethod);
eventBuilder.SetRemoveOnMethod(removeMethod);
// 定义 OnPropertyChanged 方法
var onPropertyChangedMethod = typeBuilder.DefineMethod(
"OnPropertyChanged",
MethodAttributes.Private,
null,
new[] { typeof(string) });
{
var il = onPropertyChangedMethod.GetILGenerator();
var label = il.DefineLabel();
// if (PropertyChanged == null) return;
il.Emit(OpCodes.Ldarg_0); // 加载 this
il.Emit(OpCodes.Ldfld, propertyChangedField); // 加载 this.PropertyChanged
il.Emit(OpCodes.Brfalse_S, label); // 如果为空null跳转到 label跳过通知调用
// PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
il.Emit(OpCodes.Ldarg_0); // 加载 this委托调用目标
il.Emit(OpCodes.Ldfld, propertyChangedField); // 加载 PropertyChanged 委托
il.Emit(OpCodes.Ldarg_0); // 加载 this 作为 sender 参数
il.Emit(OpCodes.Ldarg_1); // 加载 propertyName 参数string
il.Emit(OpCodes.Newobj, typeof(PropertyChangedEventArgs).GetConstructor(new[] { typeof(string) })); // 构造 new PropertyChangedEventArgs(propertyName)
il.Emit(OpCodes.Callvirt, typeof(PropertyChangedEventHandler).GetMethod("Invoke")); // 调用委托 Invoke(sender, args)
il.MarkLabel(label); // 跳转目标(如果委托为 null 则跳转至此)
il.Emit(OpCodes.Ret); // return
}
// 为每个属性生成字段 + get/set + OnPropertyChanged 调用
foreach (var kv in properties)
{
string propName = kv.Key;
Type propType = kv.Value ?? typeof(object);
var fieldBuilder = typeBuilder.DefineField("_" + propName, propType, FieldAttributes.Private);
var propertyBuilder = typeBuilder.DefineProperty(propName, PropertyAttributes.HasDefault, propType, null);
var getter = typeBuilder.DefineMethod("get_" + propName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
propType, Type.EmptyTypes);
{
// get_PropertyName()
var il = getter.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // 加载 this
il.Emit(OpCodes.Ldfld, fieldBuilder); // 读取私有字段 _PropertyName
il.Emit(OpCodes.Ret); // 返回字段值
}
var setter = typeBuilder.DefineMethod("set_" + propName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
null, new[] { propType });
{
// set_PropertyName(value)
var il = setter.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // 加载 this
il.Emit(OpCodes.Ldarg_1); // 加载 value 参数
il.Emit(OpCodes.Stfld, fieldBuilder); // 设置字段 _PropertyName = value
il.Emit(OpCodes.Ldarg_0); // 加载 this
il.Emit(OpCodes.Ldstr, propName); // 加载属性名字符串作为通知参数
il.Emit(OpCodes.Call, onPropertyChangedMethod); // 调用 OnPropertyChanged(propName)
il.Emit(OpCodes.Ret); // return
}
propertyBuilder.SetGetMethod(getter);
propertyBuilder.SetSetMethod(setter);
}
var dynamicType = typeBuilder.CreateType();
typeCache[typeName] = dynamicType;
return dynamicType;
}
#region
/// <summary>
/// 创建动态类型及其对象实例
/// </summary>
/// <param name="properties"></param>
/// <param name="typeName"></param>
/// <returns></returns>
public static object CreateObjectWithProperties(IDictionary<string, object> properties, string typeName)
{
// 如果类型已经缓存,直接返回缓存的类型
if (typeCache.ContainsKey(typeName))
{
return Activator.CreateInstance(typeCache[typeName]);
}
// 定义动态程序集和模块
var assemblyName = new AssemblyName("DynamicAssembly");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
// 定义动态类型
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
// 为每个属性名和值添加相应的属性到动态类型中
foreach (var kvp in properties)
{
string propName = kvp.Key;
object propValue = kvp.Value;
Type propType;
if (propValue is IList<Dictionary<string, object>>) // 处理数组类型
{
var nestedPropValue = (propValue as IList<Dictionary<string, object>>)[0];
var nestedType = CreateObjectWithProperties(nestedPropValue, $"{propName}Element");
propType = nestedType.GetType().MakeArrayType(); // 创建数组类型
}
else if (propValue is Dictionary<string, object> nestedProperties)
{
// 如果值是嵌套的字典,递归创建嵌套类型
propType = CreateObjectWithProperties(nestedProperties, $"{typeName}_{propName}").GetType();
}
else
{
// 如果是普通类型,使用值的类型
propType = propValue?.GetType() ?? typeof(object);
}
// 定义私有字段和公共属性
var fieldBuilder = typeBuilder.DefineField("_" + propName, propType, FieldAttributes.Private);
var propertyBuilder = typeBuilder.DefineProperty(propName, PropertyAttributes.HasDefault, propType, null);
// 定义 getter 方法
var getMethodBuilder = typeBuilder.DefineMethod(
"get_" + propName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
propType,
Type.EmptyTypes);
var getIL = getMethodBuilder.GetILGenerator();
getIL.Emit(OpCodes.Ldarg_0);
getIL.Emit(OpCodes.Ldfld, fieldBuilder);
getIL.Emit(OpCodes.Ret);
// 定义 setter 方法
var setMethodBuilder = typeBuilder.DefineMethod(
"set_" + propName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
null,
new Type[] { propType });
var setIL = setMethodBuilder.GetILGenerator();
setIL.Emit(OpCodes.Ldarg_0);
setIL.Emit(OpCodes.Ldarg_1);
setIL.Emit(OpCodes.Stfld, fieldBuilder);
setIL.Emit(OpCodes.Ret);
// 将 getter 和 setter 方法添加到属性
propertyBuilder.SetGetMethod(getMethodBuilder);
propertyBuilder.SetSetMethod(setMethodBuilder);
}
// 创建类型并缓存
var dynamicType = typeBuilder.CreateType();
typeCache[typeName] = dynamicType;
// 创建对象实例
return Activator.CreateInstance(dynamicType);
}
/// <summary>
/// 递归设置对象的属性值
/// </summary>
/// <param name="obj"></param>
/// <param name="properties"></param>
private static void SetPropertyValues(object obj, Dictionary<string, object> properties)
{
var objType = obj.GetType();
foreach (var kvp in properties)
{
var propInfo = objType.GetProperty(kvp.Key);
object value = kvp.Value;
// 如果值是嵌套的字典类型,递归处理嵌套对象
if (value is Dictionary<string, object> nestedProperties)
{
// 创建嵌套对象
var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
// 递归设置嵌套对象的值
SetPropertyValues(nestedObj, nestedProperties);
// 将嵌套对象赋值给属性
propInfo.SetValue(obj, nestedObj);
}
else
{
// 直接赋值给属性
propInfo.SetValue(obj, value);
}
}
}
/// <summary>
/// 递归设置对象的属性值(带验证)
/// </summary>
/// <param name="obj"></param>
/// <param name="properties"></param>
/// <returns></returns>
public static bool SetPropertyValuesWithValidation(object obj, IDictionary<string, object> properties)
{
var objType = obj.GetType();
bool allSuccessful = true; // 标记是否所有属性赋值成功
foreach (var kvp in properties)
{
var propName = kvp.Key;
var propValue = kvp.Value;
var propInfo = objType.GetProperty(propName);
if (propInfo == null)
{
// 属性不存在,打印警告并标记失败
SereinEnv.WriteLine(InfoType.WARN, $"属性 '{propName}' 不存在于类型 '{objType.Name}' 中,跳过赋值。");
allSuccessful = false;
continue;
}
// 检查属性类型是否与要赋的值兼容
var targetType = propInfo.PropertyType;
if (!IsCompatibleType(targetType, propValue))
{
// 如果类型不兼容,打印错误并标记失败
SereinEnv.WriteLine(InfoType.ERROR, $"无法将类型 '{propValue?.GetType().Name}' 赋值给属性 '{propName}' (Type: {targetType.Name}),跳过赋值。");
allSuccessful = false;
continue;
}
try
{
// 如果值是一个嵌套对象,递归赋值
if (propValue is Dictionary<string, object> nestedProperties)
{
var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
if (nestedObj != null && SetPropertyValuesWithValidation(nestedObj, nestedProperties))
{
propInfo.SetValue(obj, nestedObj);
}
else
{
allSuccessful = false; // 嵌套赋值失败
}
}
else if (propValue is IList<Dictionary<string, object>> list) // 处理列表
{
// 获取目标类型的数组元素类型
var elementType = propInfo.PropertyType.GetElementType();
if (elementType != null)
{
var array = Array.CreateInstance(elementType, list.Count);
for (int i = 0; i < list.Count; i++)
{
var item = Activator.CreateInstance(elementType);
if (item != null && SetPropertyValuesWithValidation(item, list[i]))
{
array.SetValue(item, i);
}
else
{
allSuccessful = false; // 赋值失败
}
}
propInfo.SetValue(obj, array);
}
}
else
{
// 直接赋值
propInfo.SetValue(obj, propValue);
}
}
catch (Exception ex)
{
SereinEnv.WriteLine(InfoType.ERROR, $"为属性 '{propName}' 赋值时发生异常:{ex.Message}");
allSuccessful = false;
}
}
return allSuccessful;
}
// 检查类型兼容性的方法(支持嵌套类型)
static bool IsCompatibleType(Type targetType, object value)
{
if (value == null)
{
// 如果值为null且目标类型是引用类型或者可空类型则兼容
return !targetType.IsValueType || Nullable.GetUnderlyingType(targetType) != null;
}
// 检查值的类型是否与目标类型相同,或是否可以转换为目标类型
if (targetType.IsAssignableFrom(value.GetType()))
{
return true;
}
// 处理数组类型
if (targetType.IsArray)
{
// 检查数组的元素类型与值的类型兼容
var elementType = targetType.GetElementType();
return value is IList<Dictionary<string, object>>; // 假设值是一个列表,具体处理逻辑在赋值时
}
// 处理嵌套类型的情况
if (value is Dictionary<string, object> && targetType.IsClass && !targetType.IsPrimitive)
{
// 如果目标类型是一个复杂对象,并且值是一个字典,可能是嵌套对象
return true; // 假设可以递归处理嵌套对象
}
return false;
}
#endregion
}
}