Files
ichni_Creator_Studio/Assets/Scripts/Extensions/ReflectionHelper.cs
TRAfoer 7a2bc844bd 我死去
目前添加了一个能够深度查找和设置反射的helper,但是Tag的系统仍然不完全,另外我觉得console可以重构了

Signed-off-by: TRAfoer <lhf190@outlook.com>
2025-12-14 12:15:07 +08:00

325 lines
11 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;
using UnityEngine;
public static class ReflectionHelper
{
private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
// --- 设置值 (支持 object) ---
public static void SetDeepValue(object target, string path, object newValue)
{
if (target == null || string.IsNullOrEmpty(path)) return;
string[] parts = path.Split('.');
SetDeepValueRecursive(target, parts, 0, newValue);
}
private static void SetDeepValueRecursive(object currentObj, string[] pathParts, int index, object newValue)
{
string memberName = pathParts[index];
Type type = currentObj.GetType();
FieldInfo field = type.GetField(memberName, Flags);
PropertyInfo prop = type.GetProperty(memberName, Flags);
if (field == null && prop == null)
{
// Debug.LogWarning($"找不到成员: {memberName}"); // 可选:调试用
return;
}
bool isLast = index == pathParts.Length - 1;
if (isLast)
{
// 到达终点:直接设置值
// 注意:这里可能需要处理类型转换,取决于你的 newValue 类型是否严格匹配
try
{
if (field != null) field.SetValue(currentObj, newValue);
else if (prop != null && prop.CanWrite) prop.SetValue(currentObj, newValue);
}
catch (Exception e) { Debug.LogError($"设置值失败 {memberName}: {e.Message}"); }
}
else
{
// 中间节点
object nextObj = field != null ? field.GetValue(currentObj) : prop.GetValue(currentObj);
if (nextObj == null) return;
SetDeepValueRecursive(nextObj, pathParts, index + 1, newValue);
// Struct 回写逻辑 (关键)
if (nextObj.GetType().IsValueType)
{
if (field != null) field.SetValue(currentObj, nextObj);
else if (prop != null && prop.CanWrite) prop.SetValue(currentObj, nextObj);
}
}
}
// --- 获取值 (支持 object) ---
public static object GetDeepValue(object target, string path)
{
object current = target;
foreach (string part in path.Split('.'))
{
if (current == null) return null;
Type type = current.GetType();
FieldInfo field = type.GetField(part, Flags);
if (field != null)
{
current = field.GetValue(current);
continue;
}
PropertyInfo prop = type.GetProperty(part, Flags);
if (prop != null)
{
current = prop.GetValue(current);
continue;
}
return null; // 路径中断
}
return current;
}
/// <summary>
/// 遍历对象的所有字段和属性(递归地),查找值与目标值匹配的参数路径。
/// </summary>
/// <param name="target">要搜索的对象实例。</param>
/// <param name="targetValue">要匹配的目标值。</param>
/// <param name="maxDepth">递归搜索的最大深度,防止无限循环。</param>
/// <returns>返回匹配参数的完整路径列表 (例如: "position.x")。</returns>
public static List<string> FindParametersByValue(object target, object targetValue, int maxDepth = 10)
{
if (target == null || targetValue == null) return new List<string>();
// 用于存储找到的路径
var results = new List<string>();
// 用于防止循环引用(比如类 A 内部引用了类 B类 B 内部又引用了类 A
var visited = new HashSet<object>();
// 启动递归搜索
FindRecursive(target, targetValue, "", 0, results, visited, maxDepth);
return results;
}
private static void FindRecursive(
object currentObject,
object targetValue,
string currentPath,
int depth,
List<string> results,
HashSet<object> visited,
int maxDepth)
{
if (currentObject == null || depth >= maxDepth) return;
// 避免无限循环和重复搜索
// 对于值类型 (Struct) 不需要加入 visited因为它们是副本
if (!currentObject.GetType().IsValueType)
{
// 如果对象已经被搜索过,则跳过
if (visited.Contains(currentObject)) return;
visited.Add(currentObject);
}
Type currentType = currentObject.GetType();
// 1. 遍历 Field 字段
foreach (var field in currentType.GetFields(Flags))
{
// 跳过内部字段,如编译器生成的 backing fields
if (field.IsDefined(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false)) continue;
object value = field.GetValue(currentObject);
string newPath = string.IsNullOrEmpty(currentPath) ? field.Name : $"{currentPath}.{field.Name}";
CheckAndRecurse(field.FieldType, value, targetValue, newPath, depth, results, visited, maxDepth);
}
// 2. 遍历 Property 属性
foreach (var prop in currentType.GetProperties(Flags).Where(p => p.CanRead))
{
// 跳过索引器
if (prop.GetIndexParameters().Length > 0) continue;
object value = prop.GetValue(currentObject);
string newPath = string.IsNullOrEmpty(currentPath) ? prop.Name : $"{currentPath}.{prop.Name}";
CheckAndRecurse(prop.PropertyType, value, targetValue, newPath, depth, results, visited, maxDepth);
}
}
/// <summary>
/// 检查当前值是否匹配,如果不是基础类型则继续递归
/// </summary>
private static void CheckAndRecurse(
Type valueType,
object value,
object targetValue,
string newPath,
int depth,
List<string> results,
HashSet<object> visited,
int maxDepth)
{
if (value == null) return;
// 【值匹配检查】
if (value.Equals(targetValue))
{
results.Add(newPath);
return;
}
// 【递归检查】
// 只有当值是一个复杂类型Class 或 Struct时才需要递归
// 排除字符串和集合List, Array等如果你需要搜索集合内部逻辑会更复杂
if (valueType.IsClass || valueType.IsValueType && !valueType.IsPrimitive)
{
// 排除 String (虽然是 Class但我们视其为基础值)
if (valueType == typeof(string)) return;
// 排除集合(如果需要支持 List<T> 的内部搜索,你需要在这里添加处理逻辑)
if (valueType.IsArray || valueType.IsGenericType && valueType.GetInterfaces().Any(i => i == typeof(System.Collections.IEnumerable))) return;
// 递归进入下一层
FindRecursive(value, targetValue, newPath, depth + 1, results, visited, maxDepth);
}
}
}
public class FastReflection
{
// 定义一个节点,用来缓存每一层的 Field 或 Property 信息
private class MemberNode
{
public FieldInfo Field;
public PropertyInfo Property;
public bool IsField => Field != null;
public MemberNode(MemberInfo info)
{
if (info is FieldInfo f) Field = f;
else if (info is PropertyInfo p) Property = p;
}
// 快速获取值
public object GetValue(object target)
{
return IsField ? Field.GetValue(target) : Property.GetValue(target);
}
// 快速设置值
public void SetValue(object target, object value)
{
if (IsField) Field.SetValue(target, value);
else if (Property != null && Property.CanWrite) Property.SetValue(target, value);
}
// 判断这一层是否是 Struct (值类型),如果是,修改后需要回写
public bool IsStructType()
{
return IsField ? Field.FieldType.IsValueType : Property.PropertyType.IsValueType;
}
}
// 访问器链条
private readonly List<MemberNode> _nodes = new List<MemberNode>();
private bool _isValid = false;
/// <summary>
/// 构造函数:初始化时进行昂贵的解析操作(只做一次)
/// </summary>
public FastReflection(object rootTarget, string path)
{
if (rootTarget == null || string.IsNullOrEmpty(path)) return;
Type currentType = rootTarget.GetType();
string[] parts = path.Split('.');
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
foreach (var part in parts)
{
// 查找字段或属性
FieldInfo field = currentType.GetField(part, flags);
PropertyInfo prop = currentType.GetProperty(part, flags);
if (field == null && prop == null)
{
Debug.LogError($"[FastReflection] 路径中断: 找不到成员 '{part}' 在类型 '{currentType.Name}' 中");
return;
}
// 创建节点并添加到链条
MemberNode node = new MemberNode(field ?? (MemberInfo)prop);
_nodes.Add(node);
// 更新 currentType 为下一层的类型
currentType = field != null ? field.FieldType : prop.PropertyType;
}
_isValid = true;
}
/// <summary>
/// 获取值(运行时极快)
/// </summary>
public float GetValue(object rootTarget)
{
if (!_isValid) return 0f;
object current = rootTarget;
for (int i = 0; i < _nodes.Count; i++)
{
current = _nodes[i].GetValue(current);
if (current == null) return 0f;
}
return (float)current; // 假设最后一定是 float
}
/// <summary>
/// 设置值(运行时极快,自动处理 Struct 回写)
/// </summary>
public void SetValue(object rootTarget, float newValue)
{
if (!_isValid) return;
// 开始递归处理回写
SetRecursive(rootTarget, 0, newValue);
}
// 递归函数:处理“获取-修改-回写”逻辑
private void SetRecursive(object currentObj, int index, float finalValue)
{
MemberNode node = _nodes[index];
// 1. 如果是链条的最后一个节点(即那个 float 变量)
if (index == _nodes.Count - 1)
{
node.SetValue(currentObj, finalValue);
return;
}
// 2. 如果是中间节点,先获取下一层对象
object nextObj = node.GetValue(currentObj);
if (nextObj == null) return;
// 3. 递归进入下一层
SetRecursive(nextObj, index + 1, finalValue);
// 4. 【关键】回写阶段
// 如果 nextObj 是 Struct值类型比如 Vector3
// 我们刚才在递归里修改了 nextObj 的副本,现在必须把它赋值回 currentObj
if (node.IsStructType())
{
node.SetValue(currentObj, nextObj);
}
}
}