关于unity:Unity-C-反射性能优化

9次阅读

共计 20567 个字符,预计需要花费 52 分钟才能阅读完成。

原文地址:https://segmentfault.com/a/11…

本篇内容次要是讲如何优化获取或设置对象字段和属性的反射性能。

先上论断:

1. 反射性能很差,比间接调用慢成千盈百倍。
2. 反射性能优化的思路是绕开反射。
3. 反射性能优化形式有: 字典缓存(Dictionary Cache)、委托(Delegate.CreateDelegate)、Expression.Lamda、IL.Emit、指针(IntPtr) 等各种形式。
4. 性能排名:间接调用 > (IL.Emit == Expression) > 委托 > 指针 > 反射
5. 通用性排名: 反射 > (委托 == 指针) > (IL.Emit == Expression) > 间接调用
6. 综合排名:(委托 == 指针) > (IL.Emit == Expression) > 反射 > 间接调用

IL.Emit 本是性能最优计划,但惋惜 IL2CPP 模式不反对 JIT 所有动静生成代码的性能都不好用 故 IL.Emit 无奈应用、Expression.Lamda 也是同样的起因不可用。
退而求其次选用性能和通用性都不错的委托,因为委托只能优化办法无奈用于字段,所以又加上了指针用于操作字段。


性能测试:

以设置对象的属性 Name 为测试条件 (obj.Name = “A”)
SetValue 办法都调整为通用的 SetValue(object target, object value) 状态。
除了反射,所有调用办法都创立委托后再调用,省略查找委托缓存的步骤。
在 Release 模式下,循环屡次调用,查看耗时。
如果间接调用耗时约为:1ms,其余形式耗时约为:

间接调用 IL.Emit Expression 委托(Delegate) 反射(MethodInfo)
1ms 1.3ms 1.3ms 2.1ms 91.7ms

指针只操作字段, 不参加该项测试

如果测试条件改为每次获取或设置值都须要通过 Dictionary 从缓存中查找字段所对应的委托办法后调用,那么工夫将大大增加,大部分工夫都节约在了字典查找上,但这也更贴近咱们失常应用的状况。

间接调用 = IL.Emit Expression 委托(Delegate) 反射(MethodInfo)
1ms 8.8ms 8.8ms 9.7ms 192.5ms

设置 string 字段测试:

间接调用 = IL.Emit Expression 指针 反射(MethodInfo)
1ms 8ms 8ms 9ms 122ms

委托只操作属性, 不参加该项测试

从测试后果能够看出
间接调用 > IL.Emit(Expression 与 IL 统一) > 委托(指针与委托互补)> 反射

基本上只有能绕开反射,性能都会有显著的晋升。


IL.Emit

长处:性能十分高。
毛病:不反对 IL2CPP。

IL.Emit 通用工具类:

用法:

var getName = ILUtil.CreateGetValue(type, "Name");
var setName = ILUtil.CreateSetValue(type, "Name");
setName(obj, "A");
string name = (string)getName(obj);

代码:

#region Intro
// Purpose: Reflection Optimize Performance
// Author: ZhangYu
// LastModifiedDate: 2022-06-11
#endregion

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;

/// <summary> IL.Emit 工具 </summary>
public static class ILUtil {private static Type[] NoTypes = new Type[] {};

    /// <summary> IL 动静增加创立新对象办法 </summary>
    public static Func<object> CreateCreateInstance(Type classType) {DynamicMethod method = new DynamicMethod(string.Empty, typeof(object), null, classType);
        if (classType.IsValueType) {
            // 实例化值类型
            ILGenerator il = method.GetILGenerator(32);
            var v0 = il.DeclareLocal(classType);
            il.Emit(OpCodes.Ldloca_S, v0);
            il.Emit(OpCodes.Initobj, classType);
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Box, classType);
            il.Emit(OpCodes.Ret);
        } else {
            // 实例化援用类型
            ConstructorInfo ctor = classType.GetConstructor(NoTypes);
            ILGenerator il = method.GetILGenerator(16);
            il.Emit(OpCodes.Newobj, ctor);
            il.Emit(OpCodes.Ret);
        }
        return method.CreateDelegate(typeof(Func<object>)) as Func<object>;
    }

    /// <summary> IL 动静增加创立新数组办法 </summary>
    public static Func<int, Array> CreateCreateArray(Type classType, int length) {DynamicMethod method = new DynamicMethod(string.Empty, typeof(Array), new Type[] { typeof(int) }, typeof(Array));
        ILGenerator il = method.GetILGenerator(16);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Newarr, classType.GetElementType());
        il.Emit(OpCodes.Ret);
        return method.CreateDelegate(typeof(Func<int, Array>)) as Func<int, Array>;
    }

    private static Dictionary<Type, Func<object>> createInstanceCache = new Dictionary<Type, Func<object>>();
    private static Dictionary<Type, Func<int, Array>> createArrayCache = new Dictionary<Type, Func<int, Array>>();

    /// <summary> IL 动态创建新的实例 </summary>
    /// <param name="classType"> 类型 </param>
    public static object CreateInstance(Type classType) {
        Func<object> createMethod = null;
        if (!createInstanceCache.TryGetValue(classType, out createMethod)) {createMethod = CreateCreateInstance(classType);
            createInstanceCache.Add(classType, createMethod);
        }
        return createMethod();}


    /// <summary> IL 动态创建新数组 </summary>
    public static Array CreateArray(Type classType, int length) {
        Func<int, Array> createMethod = null;
        if (!createArrayCache.TryGetValue(classType, out createMethod)) {createMethod = CreateCreateArray(classType, length);
            createArrayCache.Add(classType, createMethod);
        }
        return createMethod(length);
    }

    /// <summary> IL.Emit 动态创建获取字段值的办法 </summary>
    private static Func<object, object> CreateGetField(Type classType, string fieldName) {FieldInfo field = classType.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public);
        DynamicMethod method = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object) }, classType);
        ILGenerator il = method.GetILGenerator(16); // 默认大小 64 字节
        il.Emit(OpCodes.Ldarg_0);
        if (classType.IsValueType) il.Emit(OpCodes.Unbox_Any, classType);
        il.Emit(OpCodes.Ldfld, field);
        if (field.FieldType.IsValueType) il.Emit(OpCodes.Box, field.FieldType);
        il.Emit(OpCodes.Ret);
        return method.CreateDelegate(typeof(Func<object, object>)) as Func<object, object>;
    }

    /// <summary> IL.Emit 动态创建设置字段办法 </summary>
    private static Action<object, object> CreateSetField(Type classType, string fieldName) {FieldInfo field = classType.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public);
        DynamicMethod method = new DynamicMethod(string.Empty, null, new Type[] {typeof(object), typeof(object) }, classType);
        ILGenerator il = method.GetILGenerator(32); // 默认大小 64 字节
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(classType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, classType);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(field.FieldType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, field.FieldType);
        il.Emit(OpCodes.Stfld, field);
        il.Emit(OpCodes.Ret);
        return method.CreateDelegate(typeof(Action<object, object>)) as Action<object, object>;
    }

    /// <summary> IL.Emit 动态创建获取属性值的办法 </summary>
    private static Func<object, object> CreateGetProperty(Type classType, string propertyName) {PropertyInfo property = classType.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
        DynamicMethod method = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object) }, classType);
        ILGenerator il = method.GetILGenerator(32);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(classType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, classType);
        il.Emit(OpCodes.Call, property.GetGetMethod());
        if (property.PropertyType.IsValueType) il.Emit(OpCodes.Box, property.PropertyType);
        il.Emit(OpCodes.Ret);
        return method.CreateDelegate(typeof(Func<object, object>)) as Func<object, object>;
    }

    /// <summary> IL.Emit 动态创建设置属性值的办法 </summary>
    private static Action<object, object> CreateSetProperty(Type classType, string propertyName) {PropertyInfo property = classType.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
        MethodInfo methodInfo = property.GetSetMethod();
        ParameterInfo parameter = methodInfo.GetParameters()[0];
        DynamicMethod method = new DynamicMethod(string.Empty, null, new Type[] {typeof(object), typeof(object) }, classType);
        ILGenerator il = method.GetILGenerator(32);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(classType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, classType);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(parameter.ParameterType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, parameter.ParameterType);
        il.Emit(OpCodes.Call, methodInfo);
        il.Emit(OpCodes.Ret);
        return method.CreateDelegate(typeof(Action<object, object>)) as Action<object, object>;
    }

    /// <summary> IL.Emit 动态创建获取字段或属性值的办法 </summary>
    /// <param name="classType"> 对象类型 </param>
    /// <param name="fieldName"> 字段 (或属性) 名称 </param>
    public static Func<object, object> CreateGetValue(Type classType, string fieldName) {MemberInfo[] members = classType.GetMember(fieldName, BindingFlags.Instance | BindingFlags.Public);
        if (members.Length == 0) {string error = "Type [{0}] don't contains member [{1}]";
            throw new Exception(string.Format(error, classType, fieldName));
        }
        Func<object, object> getValue = null;
        switch (members[0].MemberType) {
            case MemberTypes.Field:
                getValue = CreateGetField(classType, fieldName);
                break;
            case MemberTypes.Property:
                getValue = CreateGetProperty(classType, fieldName);
                break;
            default:
                break;
        }
        return getValue;
    }

    /// <summary> IL.Emit 动态创建设置字段值 (或属性) 值的办法 </summary>
    /// <param name="classType"> 对象类型 </param>
    /// <param name="fieldName"> 字段 (或属性) 名称 </param>
    public static Action<object, object> CreateSetValue(Type classType, string fieldName) {MemberInfo[] members = classType.GetMember(fieldName, BindingFlags.Instance | BindingFlags.Public);
        if (members.Length == 0) {string error = "Type [{0}] does not contain field [{1}]";
            throw new Exception(string.Format(error, classType, fieldName));
        }
        Action<object, object> setValue = null;
        switch (members[0].MemberType) {
            case MemberTypes.Field:
                setValue = CreateSetField(classType, fieldName);
                break;
            case MemberTypes.Property:
                setValue = CreateSetProperty(classType, fieldName);
                break;
            default:
                break;
        }
        return setValue;
    }

    // Emit Getter 办法缓存字典
    private static Dictionary<Type, Dictionary<string, Func<object, object>>> getValueCache = new Dictionary<Type, Dictionary<string, Func<object, object>>>();
    // Emit Setter 办法缓存字典
    private static Dictionary<Type, Dictionary<string, Action<object, object>>> setValueCache = new Dictionary<Type, Dictionary<string, Action<object, object>>>();

    /// <summary> IL 获取对象成员的值(字段或属性) </summary>
    public static object GetValue(object obj, string fieldName) {
        // 查找一级缓存
        Type classType = obj.GetType();
        Dictionary<string, Func<object, object>> cache = null;
        if (!getValueCache.TryGetValue(classType, out cache)) {cache = new Dictionary<string, Func<object, object>>();
            getValueCache.Add(classType, cache);
        }

        // 查找二级缓存
        Func<object, object> getValue = null;
        if (!cache.TryGetValue(fieldName, out getValue)) {getValue = CreateGetValue(classType, fieldName);
            cache.Add(fieldName, getValue);
        }
        return getValue(obj);
    }

    /// <summary> IL 设置对象成员的值(字段或属性) </summary>
    public static void SetValue(object obj, string fieldName, object value) {
        // 查找一级缓存
        Type classType = obj.GetType();
        Dictionary<string, Action<object, object>> cache = null;
        if (!setValueCache.TryGetValue(classType, out cache)) {cache = new Dictionary<string, Action<object, object>>();
            setValueCache.Add(classType, cache);
        }

        // 查找二级缓存
        Action<object, object> setValue = null;
        if (!cache.TryGetValue(fieldName, out setValue)) {setValue = CreateSetValue(classType, fieldName);
            cache.Add(fieldName, setValue);
        }
        setValue(obj, value);
    }

    /// <summary> 清理已缓存 IL 办法 </summary>
    public static void ClearCache() {createInstanceCache.Clear();
        createArrayCache.Clear();
        getValueCache.Clear();
        setValueCache.Clear();}

}

Expression.Lamda

长处:性能十分高。
毛病:不反对 IL2CPP。
和 IL.Emit 一样。

Expression 通用工具类:

用法:

var wrapper = ExpressionUtil.GetPropertyWrapper(type, "Name");
wrapper.SetValue(obj, "A");
string name = (string)wrapper.GetValue(obj);

代码:

using System;
using System.Reflection;
using System.Linq.Expressions;
using System.Collections.Generic;

/// <summary> 
/// <para>Expression.Lamda 反射减速工具 </para>
/// <para>ZhangYu 2022-06-11</para>
/// </summary>
public static class ExpressionUtil {

    public class PropertyWrapper {public Func<object, object> GetValue { get; private set;}
        public Action<object, object> SetValue {get; private set;}

        public PropertyWrapper(Func<object, object> getValue, Action<object, object> setValue) {
            GetValue = getValue;
            SetValue = setValue;
        }

    }

    public static Func<object, object> CreateGetProperty(Type type, string propertyName) {var objectObj = Expression.Parameter(typeof(object), "obj");
        var classObj = Expression.Convert(objectObj, type);
        var classFunc = Expression.Property(classObj, propertyName);
        var objectFunc = Expression.Convert(classFunc, typeof(object));
        Func<object, object> getValue = Expression.Lambda<Func<object, object>>(objectFunc, objectObj).Compile();
        return getValue;
    }

    public static Action<object, object> CreateSetProperty(Type type, string propertyName) {var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
        var objectObj = Expression.Parameter(typeof(object), "obj");
        var objectValue = Expression.Parameter(typeof(object), "value");
        var classObj = Expression.Convert(objectObj, type);
        var classValue = Expression.Convert(objectValue, property.PropertyType);
        var classFunc = Expression.Call(classObj, property.GetSetMethod(), classValue);
        var setProperty = Expression.Lambda<Action<object, object>>(classFunc, objectObj, objectValue).Compile();
        return setProperty;
    }

    private static Dictionary<Type, Dictionary<string, PropertyWrapper>> cache = new Dictionary<Type, Dictionary<string, PropertyWrapper>>();

    public static PropertyWrapper GetPropertyWrapper(Type type, string propertyName) {
        // 查找一级缓存
        Dictionary<string, PropertyWrapper> wrapperDic = null;
        if (!cache.TryGetValue(type, out wrapperDic)) {wrapperDic = new Dictionary<string, PropertyWrapper>();
            cache.Add(type, wrapperDic);
        }

        // 查找二级缓存
        PropertyWrapper wrapper = null;
        if (!wrapperDic.TryGetValue(propertyName, out wrapper)) {var getValue = CreateGetProperty(type, propertyName);
            var setValue = CreateSetProperty(type, propertyName);
            wrapper = new PropertyWrapper(getValue, setValue);
            wrapperDic.Add(propertyName, wrapper);
        }
        return wrapper;
    }

    public static void ClearCache() {cache.Clear();
    }

}

委托(Delegate)

长处:性能高,通用性强 (IL2CPP 下也可应用)。
毛病:只能操作方法,无奈间接操作字段

委托通用工具类:
用法:

var wrapper = DelegateUtil.GetPropertyWrapper(type, "Name");
wrapper.SetValue(obj, "A");
string name = (string)wrapper.GetValue(obj);

代码:

using System;
using System.Reflection;
using System.Collections.Generic;

public interface IPropertyWrapper {object GetValue(object obj);
    void SetValue(object obj, object value);

}

public class PropertyWrapper<T, V> : IPropertyWrapper {

    private Func<T, V> getter;
    private Action<T, V> setter;

    public PropertyWrapper(PropertyInfo property) {getter = Delegate.CreateDelegate(typeof(Func<T, V>), property.GetGetMethod()) as Func<T, V>;
        setter = Delegate.CreateDelegate(typeof(Action<T, V>), property.GetSetMethod()) as Action<T, V>;
    }

    public V GetValue(T obj) {return getter(obj);
    }

    public void SetValue(T obj, V value) {setter(obj, value);
    }

    public object GetValue(object obj) {return GetValue((T)obj);
    }

    public void SetValue(object obj, object value) {SetValue((T)obj, (V)value);
    }

}

/// <summary> 委托工具类 ZhangYu 2022-06-10 </summary>
public static class DelegateUtil {private static Dictionary<Type, Dictionary<string, IPropertyWrapper>> cache = new Dictionary<Type, Dictionary<string, IPropertyWrapper>>();

    /// <summary> 获取属性包装器 </summary>
    public static IPropertyWrapper GetPropertyWrapper(Type type, string propertyName) {if (string.IsNullOrEmpty(propertyName)) throw new Exception("propertyName is null");
        // 查找类型
        Dictionary<string, IPropertyWrapper> proCache = null;
        if (!cache.TryGetValue(type, out proCache)) {proCache = new Dictionary<string, IPropertyWrapper>();
            cache.Add(type, proCache);
        }

        // 查找属性
        IPropertyWrapper wrapper = null;
        if (!proCache.TryGetValue(propertyName, out wrapper)) {PropertyInfo property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
            if (property == null) throw new Exception(type.Name + "type no property:" + propertyName);
            Type wrapperType = typeof(PropertyWrapper<,>).MakeGenericType(type, property.PropertyType);
            wrapper = Activator.CreateInstance(wrapperType, property) as IPropertyWrapper;
            proCache.Add(propertyName, wrapper);
        }
        return wrapper;
    }

    /// <summary> 清理缓存 </summary>
    public static void ClearCache() {cache.Clear();
    }

}

指针

长处:性能高,通用性好 (IL2CPP 下也可应用)。
毛病:临时不晓得怎么用指针操作属性(办法)。

指针通用工具类:

using System;
using System.Reflection;
using System.Collections.Generic;
using System.Runtime.InteropServices;
#if !ENABLE_MONO
using Unity.Collections.LowLevel.Unsafe;
#endif

/// <summary> 指针工具 ZhangYu 2022-06-10 </summary>
public static class PointerUtil {

    public class FieldWrapper {

        public string fieldName;
        public Type fieldType;
        public bool isValueType;
        public TypeCode typeCode;
        public int offset;
        private static bool is64Bit = Environment.Is64BitProcess;

        #region Instance Function
        /// <summary> 获取字段值 </summary>
        public unsafe object GetValue(object obj) {
            // 查看参数
            CheckObjAndFieldName(obj, fieldName);

            // 查找对象信息
            Type type = obj.GetType();

            // 获取值
            IntPtr ptr = GetPointer(obj) + offset;
            object value = null;
            switch (typeCode) {
                case TypeCode.Boolean:
                    value = *(bool*)ptr;
                    break;
                case TypeCode.Char:
                    value = *(char*)ptr;
                    break;
                case TypeCode.SByte:
                    value = *(sbyte*)ptr;
                    break;
                case TypeCode.Byte:
                    value = *(byte*)ptr;
                    break;
                case TypeCode.Int16:
                    value = *(short*)ptr;
                    break;
                case TypeCode.UInt16:
                    value = *(ushort*)ptr;
                    break;
                case TypeCode.Int32:
                    value = *(int*)ptr;
                    break;
                case TypeCode.UInt32:
                    value = *(uint*)ptr;
                    break;
                case TypeCode.Int64:
                    value = *(long*)ptr;
                    break;
                case TypeCode.UInt64:
                    value = *(ulong*)ptr;
                    break;
                case TypeCode.Single:
                    value = *(float*)ptr;
                    break;
                case TypeCode.Double:
                    value = *(double*)ptr;
                    break;
                case TypeCode.Decimal:
                    value = *(decimal*)ptr;
                    break;
                case TypeCode.DateTime:
                    value = *(DateTime*)ptr;
                    break;
                case TypeCode.DBNull:
                case TypeCode.String:
                case TypeCode.Object:
                    if (isValueType) {value = GetStruct(ptr, fieldType);
                    } else {value = GetObject(ptr);
                    }
                    break;
                default:
                    break;
            }
            return value;
        }

        /// <summary> 给字段赋值 </summary>
        public unsafe void SetValue(object obj, object value) {
            // 查看参数
            CheckObjAndFieldName(obj, fieldName);

            // 查找对象信息
            Type type = obj.GetType();

            // 获取值
            IntPtr ptr = GetPointer(obj) + offset;
            switch (typeCode) {
                case TypeCode.Boolean:
                    *(bool*)ptr = (bool)value;
                    break;
                case TypeCode.Char:
                    *(char*)ptr = (char)value;
                    break;
                case TypeCode.SByte:
                    *(sbyte*)ptr = (sbyte)value;
                    break;
                case TypeCode.Byte:
                    *(byte*)ptr = (byte)value;
                    break;
                case TypeCode.Int16:
                    *(short*)ptr = (short)value;
                    break;
                case TypeCode.UInt16:
                    *(ushort*)ptr = (ushort)value;
                    break;
                case TypeCode.Int32:
                    *(int*)ptr = (int)value;
                    break;
                case TypeCode.UInt32:
                    *(uint*)ptr = (uint)value;
                    break;
                case TypeCode.Int64:
                    *(long*)ptr = (long)value;
                    break;
                case TypeCode.UInt64:
                    *(ulong*)ptr = (ulong)value;
                    break;
                case TypeCode.Single:
                    *(float*)ptr = (float)value;
                    break;
                case TypeCode.Double:
                    *(double*)ptr = (double)value;
                    break;
                case TypeCode.Decimal:
                    *(decimal*)ptr = (decimal)value;
                    break;
                case TypeCode.DateTime:
                    *(DateTime*)ptr = (DateTime)value;
                    break;
                case TypeCode.DBNull:
                case TypeCode.String:
                case TypeCode.Object:
                    if (isValueType) {SetStruct(ptr, value);
                    } else {SetObject(ptr, value);
                    }
                    break;
                default:
                    break;
            }

        }
        #endregion

        #region Static Fucntion
        /// <summary> 获取对象地址 </summary>
        private unsafe static IntPtr GetPointer(object obj) {
        #if ENABLE_MONO
            TypedReference tr = __makeref(obj);
            return *(IntPtr*)*((IntPtr*)&tr + 1);
        #else
            ulong gcHandle;
            IntPtr ptr = (IntPtr)UnsafeUtility.PinGCObjectAndGetAddress(obj, out gcHandle);
            UnsafeUtility.ReleaseGCObject(gcHandle);
            return ptr;
        #endif
        }

        /// <summary> 将对象的地址设置到指标地址,有类型断定和援用计数,举荐在堆上操作 </summary>
        private unsafe static void SetObject(IntPtr ptr, object value) {
        #if ENABLE_MONO
            object tmp = "";
            TypedReference tr = __makeref(tmp);
            if (is64Bit) {long* p = (long*)&tr + 1;
                *p = (long)ptr;
                __refvalue(tr, object) = value;
            } else {int* p = (int*)&tr + 1;
                *p = (int)ptr;
                __refvalue(tr, object) = value;
            }
        #else
            UnsafeUtility.CopyObjectAddressToPtr(value, (void*)ptr);
        #endif
        }

        /// <summary> 设置 Struct </summary>
        private static void SetStruct(IntPtr ptr, object value) {Marshal.StructureToPtr(value, ptr, true);
        }

        /// <summary> 获取对象 </summary>
        private unsafe static object GetObject(IntPtr ptr) {
        #if ENABLE_MONO
            object tmp = "";
            TypedReference tr = __makeref(tmp);
            if (is64Bit) {long* p = (long*)&tr + 1;
                *p = (long)ptr;
                return __refvalue(tr, object);
            } else {int* p = (int*)&tr + 1;
                *p = (int)ptr;
                return __refvalue(tr, object);
            }
        #else
            return UnsafeUtility.ReadArrayElement<object>((void*)ptr, 0);
        #endif
        }

        /// <summary> 获取 Struct </summary>
        private static object GetStruct(IntPtr ptr, Type type) {return Marshal.PtrToStructure(ptr, type);
        }

        /// <summary> 查看参数 </summary>
        private static void CheckObjAndFieldName(object obj, string fieldName) {if (obj == null) throw new Exception("obj can't be null");
            if (string.IsNullOrEmpty(fieldName)) throw new Exception("FieldName can't be null or empty");
        }

        /// <summary> 获取字段地址偏移量 </summary>
        public unsafe static int GetFieldOffset(FieldInfo field) {return *(short*)(field.FieldHandle.Value + 24);
        }
        #endregion

    }

    /// <summary> 缓存 </summary>
    private static Dictionary<Type, Dictionary<string, FieldWrapper>> cache = new Dictionary<Type, Dictionary<string, FieldWrapper>>();

    public static FieldWrapper GetFieldWrapper(Type type, string fieldName) {
        // 查找一级缓存
        Dictionary<string, FieldWrapper> wrapperDic = null;
        if (!cache.TryGetValue(type, out wrapperDic)) {wrapperDic = new Dictionary<string, FieldWrapper>();
            cache.Add(type, wrapperDic);
        }

        // 查找二级缓存
        FieldWrapper wrapper = null;
        if (!wrapperDic.TryGetValue(fieldName, out wrapper)) {FieldInfo field = type.GetField(fieldName, BindingFlags.Public | BindingFlags.Instance);
            if (field == null) throw new Exception(type.Name + "field is null:" + fieldName);
            wrapper = new FieldWrapper();
            wrapper.fieldName = fieldName;
            wrapper.fieldType = field.FieldType;
            wrapper.isValueType = field.FieldType.IsValueType;
            wrapper.typeCode = Type.GetTypeCode(field.FieldType);
            wrapper.offset = FieldWrapper.GetFieldOffset(field);
            wrapperDic.Add(fieldName, wrapper);
        }
        return wrapper;
    }

    /// <summary> 清理缓存数据 </summary>
    public static void ClearCache() {cache.Clear();
    }

}

优化计划抉择:

优化计划这么多到底用哪个? 有最好的吗?(能够参考以下倡议)

1. 首选 IL.Emit 计划,性能很好,惟一的毛病是不兼容 IL2CPP 模式。

2. 次选委托 (Delegate.CreateDelegate) 计划,在 IL2CPP 模式下用于代替 IL.Emit,因为委托只能操作方法,不能操作字段,所以尽量写成 {get; set;} 的模式,如果有字段须要操作,那么再加上指针计划(IntPtr)。

终极优化伎俩:

如果以上伎俩你都尝试过了,仍然不称心,作为一个成年人你想鱼和熊掌二者兼得,性能要最高的,通用性也要最好的,还要不便易用,你能够尝试代码生成计划,这个计划惟一的毛病就是你须要不少的工夫去做代码生成工具和相干自动化工具的编写,开发成本颇高,可能要始终依据需要长期保护,属于把难题留给了本人,把不便留给了他人,前人栽树后人乘凉,做不好凉凉,做好了前人会:” 听我说谢谢你,因为有你,和煦了四季 …”。
或者你是个技术大佬,喜爱钻研技术,这点货色就是个小菜儿,以下是代码生成计划简述:

代码生成的计划:
长处:性能最高,通用性好 (IL2CPP 下也可应用)
毛病:实现简单,费时间。

通过 Unity 弱小的 Editor 本人写一套代码生成工具,把相干须要调用字段 (属性) 的性能都用代码生成。

比方我当初正在写一个二进制的数据格式,要实现对象序列化和反序列化的性能,在转换对象的过程中须要调用对象的字段(属性),假如以后操作的对象类型为:
【UserInfo】

public class UserInfo {

    public int id;
    public string name;

}

代码生成的序列化类则为:
【UserInfoSerializer】

public static class UserInfoSerializer {

    /// <summary> 序列化 </summary>
    public static byte[] Serialize(UserInfo obj) {if (obj == null) return new byte[0];
        BinaryWriter writer = new BinaryWriter();
        writer.WriteInt32(obj.id);
        writer.WriteUnicode(obj.name);
        return writer.ToBytes();}

    /// <summary> 反序列化 </summary>
    public static UserInfo Deserialize(byte[] bytes) {if (bytes == null) return null;
        UserInfo obj = new UserInfo();
        if (bytes.Length == 0) return obj;
        BinaryReader reader = new BinaryReader(bytes);
        obj.id = reader.ReadInt32();
        obj.name = reader.ReadUnicode();
        return obj;
    }

}

测试代码:

private void SerializeTest() {
    // 初始化对象
    UserInfo user = new UserInfo();
    user.id = 1001;
    user.name = "A";

    // 序列化
    byte[] bytes = UserInfoSerializer.Serialize(user);

    // 反序列化
    UserInfo u = UserInfoSerializer.Deserialize(bytes);

    // 输入数据
    print(u.id);   // 1001
    print(u.name); // "A"
}

测试代码通过,这个代码生成计划是行得通的,因为是具体类型间接调用,没有任何反射和字典查找的耗费,性能极好,接下来就是要做易用性和自动化解决。

咱们在 Unity Editor 中须要实现的性能大抵如下:

在 Unity 中,选中一个咱们须要序列化的类,右键菜单 > 生成序列化帮忙类,以便调用。

这种生成最好别对原来的代码进行任何批改,每次生成只参考指标类,生成序列化类,不对指标类进行改变,这样从新生成的时候只有笼罩生成类就好。

总结:

有时候看似最笨,最麻烦的计划,反而是最好的,兴许这就是返璞归真,重剑无锋,大巧不工吧。

想要最佳的性能,间接调用才是最好的计划,如何解决间接调用的问题,用代码生成,Unity 有着弱小的 Editor 和各种接口,如果有工夫和信念,能够尝试一下代码生成计划,但因为可能耗费很多工夫,这里就不举荐了,各位依据本人的爱好抉择就好。总之先做完,再欠缺。

正文完
 0