You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
7.3 KiB
188 lines
7.3 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Linq.Expressions; |
|
using System.Reflection; |
|
|
|
namespace TouchSocket.Core |
|
{ |
|
/// <summary> |
|
/// 动态成员访问器 |
|
/// </summary> |
|
/// <typeparam name="T"></typeparam> |
|
public class MemberAccessor<T> : MemberAccessor |
|
{ |
|
/// <summary> |
|
/// 动态成员访问器 |
|
/// </summary> |
|
public MemberAccessor() : base(typeof(T)) |
|
{ |
|
|
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 动态成员访问器 |
|
/// </summary> |
|
public class MemberAccessor : IMemberAccessor |
|
{ |
|
Func<object, string, object> GetValueDelegate; |
|
|
|
Action<object, string, object> SetValueDelegate; |
|
|
|
/// <summary> |
|
/// 动态成员访问器 |
|
/// </summary> |
|
/// <param name="type"></param> |
|
public MemberAccessor(Type type) |
|
{ |
|
Type = type; |
|
this.OnGetFieldInfes = (t) => { return t.GetFields(); }; |
|
this.OnGetProperties = (t) => { return t.GetProperties(); }; |
|
} |
|
|
|
private Dictionary<string, FieldInfo> dicFieldInfes; |
|
private Dictionary<string, PropertyInfo> dicProperties; |
|
/// <summary> |
|
/// 构建 |
|
/// </summary> |
|
public void Build() |
|
{ |
|
if (GlobalEnvironment.OptimizedPlatforms.HasFlag(OptimizedPlatforms.Unity)) |
|
{ |
|
dicFieldInfes = this.OnGetFieldInfes.Invoke(Type).ToDictionary(a => a.Name); |
|
dicProperties = this.OnGetProperties.Invoke(Type).ToDictionary(a => a.Name); |
|
} |
|
|
|
GetValueDelegate = GenerateGetValue(); |
|
SetValueDelegate = GenerateSetValue(); |
|
} |
|
|
|
/// <summary> |
|
/// 获取属性 |
|
/// </summary> |
|
public Func<Type, PropertyInfo[]> OnGetProperties { get; set; } |
|
|
|
/// <summary> |
|
/// 获取字段 |
|
/// </summary> |
|
public Func<Type, FieldInfo[]> OnGetFieldInfes { get; set; } |
|
|
|
/// <summary> |
|
/// 所属类型 |
|
/// </summary> |
|
public Type Type { get; } |
|
|
|
/// <inheritdoc/> |
|
public object GetValue(object instance, string memberName) |
|
{ |
|
return GetValueDelegate(instance, memberName); |
|
} |
|
|
|
/// <inheritdoc/> |
|
public void SetValue(object instance, string memberName, object newValue) |
|
{ |
|
SetValueDelegate(instance, memberName, newValue); |
|
} |
|
|
|
private Func<object, string, object> GenerateGetValue() |
|
{ |
|
if (GlobalEnvironment.OptimizedPlatforms.HasFlag(OptimizedPlatforms.Unity)) |
|
{ |
|
return (obj, key) => |
|
{ |
|
if (dicFieldInfes.TryGetValue(key, out var value1)) |
|
{ |
|
return value1.GetValue(obj); |
|
} |
|
if (dicProperties.TryGetValue(key, out var value2)) |
|
{ |
|
return value2.GetValue(obj); |
|
} |
|
return default; |
|
}; |
|
} |
|
|
|
var instance = Expression.Parameter(typeof(object), "instance"); |
|
var memberName = Expression.Parameter(typeof(string), "memberName"); |
|
var nameHash = Expression.Variable(typeof(int), "nameHash"); |
|
var calHash = Expression.Assign(nameHash, Expression.Call(memberName, typeof(object).GetMethod("GetHashCode"))); |
|
var cases = new List<SwitchCase>(); |
|
foreach (var propertyInfo in this.OnGetFieldInfes.Invoke(Type)) |
|
{ |
|
var property = Expression.Field(Expression.Convert(instance, Type), propertyInfo.Name); |
|
var propertyHash = Expression.Constant(propertyInfo.Name.GetHashCode(), typeof(int)); |
|
|
|
cases.Add(Expression.SwitchCase(Expression.Convert(property, typeof(object)), propertyHash)); |
|
} |
|
foreach (var propertyInfo in this.OnGetProperties.Invoke(Type)) |
|
{ |
|
var property = Expression.Property(Expression.Convert(instance, Type), propertyInfo.Name); |
|
var propertyHash = Expression.Constant(propertyInfo.Name.GetHashCode(), typeof(int)); |
|
|
|
cases.Add(Expression.SwitchCase(Expression.Convert(property, typeof(object)), propertyHash)); |
|
} |
|
if (cases.Count == 0) |
|
{ |
|
return (a, b) => default; |
|
} |
|
var switchEx = Expression.Switch(nameHash, Expression.Constant(null), cases.ToArray()); |
|
var methodBody = Expression.Block(typeof(object), new[] { nameHash }, calHash, switchEx); |
|
|
|
return Expression.Lambda<Func<object, string, object>>(methodBody, instance, memberName).Compile(); |
|
} |
|
|
|
private Action<object, string, object> GenerateSetValue() |
|
{ |
|
if (GlobalEnvironment.OptimizedPlatforms.HasFlag(OptimizedPlatforms.Unity)) |
|
{ |
|
return (obj, key, value) => |
|
{ |
|
if (dicFieldInfes.TryGetValue(key, out var value1)) |
|
{ |
|
value1.SetValue(obj, value); |
|
} |
|
if (dicProperties.TryGetValue(key, out var value2)) |
|
{ |
|
value2.SetValue(obj, value); |
|
} |
|
}; |
|
} |
|
|
|
var instance = Expression.Parameter(typeof(object), "instance"); |
|
var memberName = Expression.Parameter(typeof(string), "memberName"); |
|
var newValue = Expression.Parameter(typeof(object), "newValue"); |
|
var nameHash = Expression.Variable(typeof(int), "nameHash"); |
|
var calHash = Expression.Assign(nameHash, Expression.Call(memberName, typeof(object).GetMethod("GetHashCode"))); |
|
var cases = new List<SwitchCase>(); |
|
foreach (var propertyInfo in this.OnGetFieldInfes.Invoke(Type)) |
|
{ |
|
var property = Expression.Field(Expression.Convert(instance, Type), propertyInfo.Name); |
|
var setValue = Expression.Assign(property, Expression.Convert(newValue, propertyInfo.FieldType)); |
|
var propertyHash = Expression.Constant(propertyInfo.Name.GetHashCode(), typeof(int)); |
|
|
|
cases.Add(Expression.SwitchCase(Expression.Convert(setValue, typeof(object)), propertyHash)); |
|
} |
|
foreach (var propertyInfo in this.OnGetProperties(Type)) |
|
{ |
|
if (!propertyInfo.CanWrite) |
|
{ |
|
continue; |
|
} |
|
var property = Expression.Property(Expression.Convert(instance, Type), propertyInfo.Name); |
|
var setValue = Expression.Assign(property, Expression.Convert(newValue, propertyInfo.PropertyType)); |
|
var propertyHash = Expression.Constant(propertyInfo.Name.GetHashCode(), typeof(int)); |
|
|
|
cases.Add(Expression.SwitchCase(Expression.Convert(setValue, typeof(object)), propertyHash)); |
|
} |
|
if (cases.Count == 0) |
|
{ |
|
return (a, b, c) => { }; |
|
} |
|
var switchEx = Expression.Switch(nameHash, Expression.Constant(null), cases.ToArray()); |
|
var methodBody = Expression.Block(typeof(object), new[] { nameHash }, calHash, switchEx); |
|
|
|
return Expression.Lambda<Action<object, string, object>>(methodBody, instance, memberName, newValue).Compile(); |
|
} |
|
} |
|
} |