C#托管类型指针,提高反射效率

最近在做数据的处理,需要记录数据状态,用于用户的撤销和反撤销处理。

为了开发方便记录数据当然优先选择反射,采用指针数据加反射,数据容器使用DataBuffer,测试速度比Newtonsoft.Josn序列化快10几倍,但是平时开发的场景较多,保存一次需要50多MS。大多时间消耗在字段值读取和写入上面,对于强迫症来说,不能忍。

网上查关于C#的反射优化多大是使用属性,Emit,创建委托之类的

好在查询的时候发现的C#的不常用关键字 __makeref 和 __refvalue 

然后花了两天时间捣鼓研究,发现 FieldInfo中有个字段叫RuntimeFieldHandle 的,其值是内存地址,好家伙,根据以前开发外挂的经验去做内存分析,看来微软还是比较良心声明也比较简单,32位的偏移位置地址记录在第8个字节那里,64位的偏移位置地址记录在第12字节那里。

而获取类的地址就比较简单了, 使用__makeref获取类实例的引用地址,然后通过引用地址再跳转一次就找到了。

那么动起手来,封装一个操作类,这里我们还要判定程序运行位数,使用Environment.Is64BitProcess;

代码就不详细介绍了,直接贴出来


using System;

using System.Collections.Generic;

using System.Text;

namespace huqiang.Data

{

    public class UnsafeOperation

    {

        static bool is64 = Environment.Is64BitProcess;

        /// <summary>

        /// 获取值类型的地址

        /// </summary>

        /// <param name="tf"></param>

        /// <returns></returns>

        public unsafe static IntPtr GetValueAddr(TypedReference tf)

        {

            return *(IntPtr*)&tf;

        }

        /// <summary>

        /// 获取引用类型的地址

        /// </summary>

        /// <param name="tf"></param>

        /// <returns></returns>

        public unsafe static IntPtr GetObjectAddr(TypedReference tf)

        {

            return *(IntPtr*)*(IntPtr*)&tf;

        }

        /// <summary>

        ///  获取引用类型的地址

        /// </summary>

        /// <param name="obj"></param>

        /// <returns></returns>

        public unsafe static IntPtr GetObjectAddr(object obj)

        {

            TypedReference tf = __makeref(obj);

            return *(IntPtr*)*(IntPtr*)&tf;

        }

        /// <summary>

        ///  程序的字段声明偏移位置

        /// </summary>

        /// <param name="ptr"></param>

        /// <returns></returns>

        public unsafe static int GetFeildOffset(IntPtr ptr)

        {

            if (is64)

                return *(Int16*)(ptr + 12) + 8;

            return *(Int16*)(ptr + 8) + 4;

        }

        /// <summary>

        ///  将对象的地址设置到目标地址,不会有类型判定和引用计数,操作堆数据会造成GC判定错误,推荐在栈上操作

        /// </summary>

        /// <param name="tar"></param>

        /// <param name="obj"></param>

        public unsafe static void UnsafeSetObject(void* tar, object obj)

        {

            TypedReference tf = __makeref(obj);

            if (is64)

                *(long*)tar = *(long*)(*(IntPtr*)&tf);

            else

                *(int*)tar = *(int*)(*(IntPtr*)&tf);

        }

        /// <summary>

        /// 将对象的地址设置到目标地址,有类型判定和引用计数,推荐在堆上操作

        /// </summary>

        /// <param name="tar"></param>

        /// <param name="obj"></param>

        public unsafe static void SetObject(IntPtr tar, object obj)

        {

            object tmp = "";

            TypedReference tr = __makeref(tmp);

            if(is64)

            {

                long* p = (long*)&tr;

                *p = (long)tar;

                __refvalue(tr, object) = obj;

            }

            else

            {

                int* p = (int*)&tr;

                *p = (int)tar;

                __refvalue(tr, object) = obj;

            }

        }

        /// <summary>

        /// 获取目标地址的对象

        /// </summary>

        /// <param name="tar"></param>

        /// <returns></returns>

        public unsafe static object GetObject(void* tar)

        {

            object tmp = "";

            TypedReference tr = __makeref(tmp);//new TypedReference();

            if(is64)

            {

                long* p = (long*)&tr;

                *p = (long)tar;

                return __refvalue(tr, object);

            }

            else

            {

                int* p = (int*)&tr;

                *p = (int)tar;

                return __refvalue(tr, object);

            }

        }

        /// <summary>

        ///  获取32位程序的字段声明偏移位置

        /// </summary>

        /// <param name="ptr"></param>

        /// <returns></returns>

        public unsafe static int GetFeildOffset32(IntPtr ptr)

        {

            return *(Int16*)(ptr + 8) + 4;

        }

        /// <summary>

        /// 将对象的地址设置到目标地址,不会有类型判定和引用计数,操作堆数据会造成GC判定错误,推荐在栈上操作

        /// </summary>

        /// <param name="tar"></param>

        /// <param name="obj"></param>

        public unsafe static void UnsafeSetObject32(int* tar, object obj)

        {

            TypedReference tf = __makeref(obj);

            *tar = *(int*)(*(IntPtr*)&tf);

        }

        /// <summary>

        /// 将对象的地址设置到目标地址,有类型判定和引用计数,推荐在堆上操作

        /// </summary>

        /// <param name="tar"></param>

        /// <param name="obj"></param>

        public unsafe static void SetObject32(int* tar, object obj)

        {

            object tmp = "";

            TypedReference tr = __makeref(tmp);

            int* p = (int*)&tr;

            *p = (int)tar;

            __refvalue(tr, object) = obj;

        }

        /// <summary>

        /// 获取目标地址的对象

        /// </summary>

        /// <param name="tar"></param>

        /// <returns></returns>

        public unsafe static object GetObject32(int* tar)

        {

            object tmp = "";

            TypedReference tr = __makeref(tmp);//new TypedReference();

            int* p = (int*)&tr;

            *p = (int)tar;

            return __refvalue(tr, object);

        }

        /// <summary>

        /// 获取64位程序的字段声明偏移位置

        /// </summary>

        /// <param name="ptr"></param>

        /// <returns></returns>

        public unsafe static int GetFeildOffset64(IntPtr ptr)

        {

            return *(Int16*)(ptr + 12) + 8;

        }

        /// <summary>

        /// 将对象的地址设置到目标地址,不会有类型判定和引用计数,操作堆数据会造成GC判定错误,推荐在栈上操作

        /// </summary>

        /// <param name="tar"></param>

        /// <param name="obj"></param>

        public unsafe static void UnsafeSetObject64(long* tar, object obj)

        {

            TypedReference tf = __makeref(obj);

            *tar = *(long*)(*(IntPtr*)&tf);

        }

        /// <summary>

        /// 将对象的地址设置到目标地址,有类型判定和引用计数,推荐在堆上操作

        /// </summary>

        /// <param name="tar"></param>

        /// <param name="obj"></param>

        public unsafe static void SetObject64(long* tar, object obj)

        {

            object tmp = "";

            TypedReference tr = __makeref(tmp);

            long* p = (long*)&tr;

            *p = (long)tar;

            __refvalue(tr, object) = obj;

        }

        /// <summary>

        /// 获取目标地址的对象

        /// </summary>

        /// <param name="tar"></param>

        /// <returns></returns>

        public unsafe static object GetObject64(void* tar)

        {

            object tmp = "";

            TypedReference tr = __makeref(tmp);//new TypedReference();

            long* p = (long*)&tr;

            *p = (long)tar;

            return __refvalue(tr, object);

        }

    }

}



然后我们写一个控制台测试程序

using System;

using huqiang.Data;

namespace ConsoleTest

{

    class Program

    {

        public class Wepon

        {

            public string Name;

        }

        public class People

        {

            public int Age;

            public int Tow { get; set; }

            public byte Sex;

            public string Name;

            public long Time;

            public Wepon wepon;

        }

      unsafe static void Main(string[] args)

        {

            Type typ = typeof(People);

            var nf = typ.GetField("Age");

            var handle = nf.FieldHandle;

            int os1 = UnsafeOperation.GetFeildOffset(handle.Value);

            nf = typ.GetField("Sex");

            var handle2 = nf.FieldHandle;

            int os2 = UnsafeOperation.GetFeildOffset(handle2.Value);

            nf = typ.GetField("wepon");

            var handle3 = nf.FieldHandle;

            int os3 = UnsafeOperation.GetFeildOffset(handle3.Value);

            nf = typ.GetField("Name");

            var handle4 = nf.FieldHandle;

            int os4 = UnsafeOperation.GetFeildOffset(handle4.Value);

            People people = new People();

            people.Age = 16;

            people.Name = "Hello";

            people.Tow = 6;

            people.Sex = 4;

            people.Time = -1;

            Wepon wepon = new Wepon();

            wepon.Name = "机枪";

            TypedReference tf = __makeref(people);

            IntPtr c = UnsafeOperation.GetObjectAddr(tf);

            *(int*)(c + os1) = 200;//设置年龄

            *(byte*)(c + os2) = 3;//设置性别

            UnsafeOperation.SetObject(c + os3, wepon);//设置武器类

            UnsafeOperation.SetObject(c + os4, "打的不错哦!呵呵.");//设置名字

            Console.WriteLine(people.Age);

            Console.WriteLine(people.Sex);

            Console.WriteLine(people.wepon.Name);

            Console.WriteLine(people.Name);

            Console.ReadKey(false);

        }

    }

}

运行效果如下


将代码转移至Unity中发现内存的偏移地址不一样,使用VS无法直接查看内存,只好祭出Cheat Engine,发现其字段数据偏移地址为24 

        public unsafe static Int16 GetFeildOffset(IntPtr ptr)

        {

              return *(Int16*)(ptr + 24);

        }

其对象地址需要再偏移一个长整型

public unsafe static object GetObject(IntPtr tar)

        {

            object tmp = "";

            TypedReference tr = __makeref(tmp);//new TypedReference();

                long* p = (long*)&tr;

                p++;

                *p = (long)tar;

                return __refvalue(tr, object);

        }

最后测试结果运行效率提升1倍,勉强能接受吧

创作不易,转载请声明原创地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,273评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,349评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,709评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,520评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,515评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,158评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,755评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,660评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,203评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,287评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,427评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,122评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,801评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,272评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,393评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,808评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,440评论 2 359

推荐阅读更多精彩内容