您当前的位置:首页 > 文章 > C#(含Unity)unsafe指针快速

C#(含Unity)unsafe指针快速反射第四篇(整合篇 )

作者:流觞曲水醉江蛟 时间:2023-09-18 阅读数:164 人阅读

Unity3D(已完成iL2cpp和Mono的编译)请访问:smopu/unity3d_quick_reflection: 在unity3D中使用指针进行快速反射 (github.com)

.Net Framework 和.Net Core请访问:smopu/CSharp_QuickReflection: C# .Net Framework Quick Reflection 快速反射 (github.com)

整合的解决方案已经放在上面地址中了。其中Unity通过IL2Cpp编译,保证在AOP的情况下,能顺利使用(包括iOS平台、WebGL平台)

这篇文章就简单介绍一下上面解决方案的基本内容。

看到前面三篇文章的读者大部分都是Unity使用者哈,其实本人也是unity开发者之一,之所以还研究.Net Framework 和.Net Core是因为以下三个原因:

1、本人先学的.Net Framework后学的Unity,所以概念先入为主了。

2、据不可靠消息称,Unity将在2024年支持.Net Core。

3、鉴于Unity对中国开发者的各种区别对待,本人随时保持脱离该引擎的准备,但大概率不会脱离C#这门语言,所以支持的内容必须保证.Net Framework 和.Net Core。

看读者评论不多,估计一些基本概念本人还没有讲清楚。实际上使用C#,很多情况下不会使用指针,但是大部分框架都会用到反射,C#、Java的各种框架和解决方案中,反射都是很基础的功能,尤其是涉及到面向切面编程(Aspect Oriented Programming,缩写为:AOP)、控制反转(Inversion of Control,缩写为IoC)的概念。

指针的基础用法就是两个:取地址和取指针的值

取地址:

int a = 1; int* p = &a; 

取指针的值:

int a = *p; 

然后指针可以强制转换,我们可以把int类型的指针当作float类型取值:

int a = 1; int* p = &a; float b = *(float*)p; 

我们通过反编译看float在C#中的源代码,看GetHashCode这个函数就能发现这样的操作

Single.GetHashCod()源代码

Single.GetHashCod()的作用就是把float类型转换成一个int类型 ,转换方法就是把float类型中的数据,当作int类型取一遍。学习过计算机理论课程的朋友肯定知道IEEE浮点数的概念,本人也有一篇文章:

流觞曲水醉江蛟:C#浮点数
比如float数字6.4的二进制代码是 01000000110011001100110011001101,我们把这一段代码当作整数读取就是1087163597。以下代码就能看出来

float a = 6.4f; int b = *(int*)(&a);//1087163597  int b2 = a.GetHashCode();//1087163597 

在C#中,引用类型的对象(包括包装类)会有一个类型指针的内容在对象最开始。

比如这样的类:

public class G { public int a = 12; public string b = "1sdjfoisdjf"; public Vector3 c = new Vector3(1.1f, 2.2f, 3.3f); } 

这样的对象在内存中它是这样的

在C#中,关于引用类型的指针,统一是void*,无论它是什么类型。比如b字段是字符串类型,但是它的指针还是void*而不是string*。同样的,在IL语言中,void*就是Object类型,它们的区别在于,Object类型会随着GC改变内存地址(分代GC),但是Unity用的是mono(IL2cpp在内存概念上跟Mono一致),没有分代GC,所以对象地址不会改变。因此在Unity中,void*和Object完全一致。

而IntPtr代表所有指针,是所有指针的元类型。

解决分代GC的问题我们在第二篇已经提到过了,这里不多说了。

在.Net系统库中,原生的反射(System.Reflection),会区分字段和属性,本人这个解决方案把这两个合并在一起。可以看我工程里面的TypeAddrFieldAndProperty.cs文件。

因为合并在一起,所以处理时,还是会判断一下:

上面说过了void*等同于object,以下代码就能实现对原生反射的平替。

GetValue也是一样的啦。

当分析一个类的时候,我把一个类型的所有字段和属性全部取出,把各种需要的内容(偏移量、是否值类型(否就是引用类型)、当前字段或者属性的类型等)缓存,详见TypeAddrReflectionWrapper类。

数组需要单独处理一下,详见ArrayWrapManager.cs,我用了一个基类IArrayWrap,继承这个基类的类型就两个,一个是1维数组的类(ArrayWrapRankOne),另一个是多维数组的类(ArrayWrapRank)。

主要内容在第三篇讲解了,我们使用指针反射可以反射任意维度的数组。关于多维数组的内容还可以看我另一篇文章:流觞曲水醉江蛟:C#反射序列化多维数组

接下来还有一篇性能比较的文章,我将比较指针反射和原生反射、Emit技术、表达式树技术之间的性能。还请广大读者多提意见。

本站大部分文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了您的权益请来信告知我们删除。邮箱:1451803763@qq.com