Files
Hasebe/Library/PackageCache/com.annulusgames.alchemy@1402075f473d/Editor/Internal/ReflectionHelper.cs
T
2026-09-21 12:15:57 +03:00

315 lines
14 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Alchemy.Editor
{
internal static class ReflectionHelper
{
static readonly Dictionary<(Type, string, BindingFlags, bool), FieldInfo> cacheFieldInfo = new();
static readonly Dictionary<(Type, string, BindingFlags, bool), MethodInfo> cacheMethodInfo = new();
static readonly Dictionary<(Type, string, BindingFlags, bool), PropertyInfo> cachePropertyInfo = new();
static readonly Dictionary<(Type, BindingFlags, bool), MemberInfo[]> cacheAllMembers = new();
static readonly Dictionary<(Type, string), Func<object, object>> cacheGetFieldValue = new();
static readonly Dictionary<(Type, string), Func<object, object>> cacheGetPropertyValue = new();
static readonly Dictionary<(Type, string), Func<object, object>> cacheGetMethodValue = new();
public static Func<object, object> CreateGetter(FieldInfo fieldInfo)
{
if (fieldInfo == null) return null;
if (fieldInfo.IsStatic)
{
var body = Expression.Convert(Expression.MakeMemberAccess(null, fieldInfo), typeof(object));
var lambda = Expression.Lambda<Func<object>>(body).Compile();
return _ => lambda();
}
if (fieldInfo.DeclaringType != null)
{
var objParam = Expression.Parameter(typeof(object), "obj");
var tParam = Expression.Convert(objParam, fieldInfo.DeclaringType);
var body = Expression.Convert(Expression.MakeMemberAccess(tParam, fieldInfo), typeof(object));
return Expression.Lambda<Func<object, object>>(body, objParam).Compile();
}
return null;
}
public static Func<object, object> CreateGetter(PropertyInfo propertyInfo)
{
if (propertyInfo == null) return null;
if (propertyInfo.GetGetMethod(true).IsStatic)
{
var body = Expression.Convert(Expression.MakeMemberAccess(null, propertyInfo), typeof(object));
var lambda = Expression.Lambda<Func<object>>(body).Compile();
return _ => lambda();
}
if (propertyInfo.DeclaringType != null)
{
var objParam = Expression.Parameter(typeof(object), "obj");
var tParam = Expression.Convert(objParam, propertyInfo.DeclaringType);
var body = Expression.Convert(Expression.MakeMemberAccess(tParam, propertyInfo), typeof(object));
return Expression.Lambda<Func<object, object>>(body, objParam).Compile();
}
return null;
}
public static Func<object, object> CreateGetter(MethodInfo methodInfo)
{
if (methodInfo == null) return null;
if (methodInfo.IsStatic)
{
var body = Expression.Convert(Expression.Call(null, methodInfo), typeof(object));
var lambda = Expression.Lambda<Func<object>>(body).Compile();
return _ => lambda();
}
if (methodInfo.DeclaringType != null)
{
var objParam = Expression.Parameter(typeof(object), "obj");
var tParam = Expression.Convert(objParam, methodInfo.DeclaringType);
var body = Expression.Convert(Expression.Call(tParam, methodInfo), typeof(object));
return Expression.Lambda<Func<object, object>>(body, objParam).Compile();
}
return null;
}
public static object GetFieldValue(object target, Type type, string name, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
{
if (!cacheGetFieldValue.TryGetValue((type, name), out var value))
{
var info = type.GetField(name, bindingAttr);
value = CreateGetter(info);
cacheGetFieldValue.Add((type, name), value);
}
return value?.Invoke(target);
}
public static object GetPropertyValue(object target, Type type, string name, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
{
if (!cacheGetPropertyValue.TryGetValue((type, name), out var value))
{
var info = type.GetProperty(name, bindingAttr);
value = CreateGetter(info);
cacheGetPropertyValue.Add((type, name), value);
}
return value?.Invoke(target);
}
public static object GetMethodValue(object target, Type type, string name, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
{
if (!cacheGetMethodValue.TryGetValue((type, name), out var value))
{
var info = type.GetMethod(name, bindingAttr);
value = CreateGetter(info);
cacheGetMethodValue.Add((type, name), value);
}
return value?.Invoke(target);
}
public static FieldInfo GetField(Type type, string name, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, bool includingBaseNonPublic = false)
{
if (!cacheFieldInfo.TryGetValue((type, name, bindingAttr, includingBaseNonPublic), out var info))
{
if (includingBaseNonPublic)
{
info = GetAllFieldsIncludingBaseNonPublic(type, bindingAttr).FirstOrDefault(x => x.Name == name);
}
else
{
info = type.GetField(name, bindingAttr);
}
cacheFieldInfo.Add((type, name, bindingAttr, includingBaseNonPublic), info);
}
return info;
}
static IEnumerable<Type> EnumerateTypeHierarchy(Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
public static IEnumerable<FieldInfo> GetAllFieldsIncludingBaseNonPublic(Type type, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
{
return EnumerateTypeHierarchy(type).Reverse().SelectMany(t => t.GetFields(bindingAttr | BindingFlags.DeclaredOnly));
}
public static PropertyInfo GetProperty(Type type, string name, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, bool includingBaseNonPublic = false)
{
if (!cachePropertyInfo.TryGetValue((type, name, bindingAttr, includingBaseNonPublic), out var info))
{
if (includingBaseNonPublic)
{
info = GetAllPropertiesIncludingBaseNonPublic(type, bindingAttr).FirstOrDefault(x => x.Name == name);
}
else
{
info = type.GetProperty(name, bindingAttr);
}
cachePropertyInfo.Add((type, name, bindingAttr, includingBaseNonPublic), info);
}
return info;
}
public static IEnumerable<PropertyInfo> GetAllPropertiesIncludingBaseNonPublic(Type type, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
{
return EnumerateTypeHierarchy(type).Reverse().SelectMany(t => t.GetProperties(bindingAttr | BindingFlags.DeclaredOnly));
}
public static MethodInfo GetMethod(Type type, string name, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, bool includingBaseNonPublic = false)
{
if (!cacheMethodInfo.TryGetValue((type, name, bindingAttr, includingBaseNonPublic), out var info))
{
if (includingBaseNonPublic)
{
info = GetAllMethodsIncludingBaseNonPublic(type, bindingAttr).FirstOrDefault(x => x.Name == name);
}
else
{
info = type.GetMethods(bindingAttr).Where(x => x.Name == name).FirstOrDefault();
}
cacheMethodInfo.Add((type, name, bindingAttr, includingBaseNonPublic), info);
}
return info;
}
public static IEnumerable<MethodInfo> GetAllMethodsIncludingBaseNonPublic(Type type, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
{
return EnumerateTypeHierarchy(type).Reverse().SelectMany(t => t.GetMethods(bindingAttr | BindingFlags.DeclaredOnly));
}
public static object GetValue(object target, string name, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, bool allowProperty = true, bool allowMethod = true)
{
if (target == null) return null;
Type type = target.GetType();
object result;
while (type != null)
{
result = GetFieldValue(target, type, name, bindingAttr);
if (result != null) return result;
if (allowProperty)
{
result = GetPropertyValue(target, type, name, bindingAttr);
if (result != null) return result;
}
if (allowMethod)
{
result = GetMethodValue(target, type, name, bindingAttr);
if (result != null) return result;
}
type = type.BaseType;
}
return null;
}
public static object GetValue(object target, string name, int index)
{
if (GetValue(target, name, allowMethod: false) is not IEnumerable enumerable) return null;
IEnumerator enumerator = enumerable.GetEnumerator();
for (int i = 0; i <= index; i++)
{
if (!enumerator.MoveNext()) return null;
}
return enumerator.Current;
}
public static bool GetValueBool(object target, string name)
{
if (GetValue(target, name) is bool cond)
{
return cond;
}
return false;
}
public static MemberInfo[] GetMembers(Type type, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, bool includingBaseNonPublic = false)
{
if (cacheAllMembers.TryGetValue((type, bindingAttr, includingBaseNonPublic), out var memberInfoArray))
{
return memberInfoArray;
}
else
{
if (includingBaseNonPublic)
{
memberInfoArray = GetMembersIncludingBaseNonPublic(type, bindingAttr).ToArray();
}
else
{
memberInfoArray = type.GetMembers(bindingAttr);
}
cacheAllMembers.Add((type, bindingAttr, includingBaseNonPublic), memberInfoArray);
return memberInfoArray;
}
}
public static IEnumerable<MemberInfo> GetMembersIncludingBaseNonPublic(Type type, BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
{
return EnumerateTypeHierarchy(type).Reverse().SelectMany(t => t.GetMembers(bindingAttr | BindingFlags.DeclaredOnly));
}
public static object Invoke(object target, string name, params object[] parameters)
{
if (target == null) return false;
Type type = target.GetType();
BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
while (type != null)
{
var m = GetMethod(type, name, bindingAttr);
if (m != null) return m.Invoke(m.IsStatic ? null : target, parameters);
type = type.BaseType;
}
return false;
}
public static bool TryInvoke(object target, string name, object[] parameters, out object result)
{
if (target == null)
{
result = default;
return false;
}
Type type = target.GetType();
BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
while (type != null)
{
var m = GetMethod(type, name, bindingAttr);
if (m != null)
{
result = m.Invoke(m.IsStatic ? null : target, parameters);
return true;
}
type = type.BaseType;
}
result = default;
return false;
}
public static object GetCollectionValue(object target, int index)
{
var type = target.GetType();
if (type.IsArray) return ((Array)target).GetValue(index);
else if (target is IList list) return list[index];
return null;
}
}
}