Unity&&C#学习笔记-反射

C#对象的内存布局


类:是一种类型描述,描述了这个类型有哪些数据组成,同时描述一些成员函数;
类的实例: new类();具体的内存对象,---》一块内存【int(age),int(sex), name(string)】
这块内存是所有数据成员的集合;


类的成员函数属于代码指令,编译完成以后,会编程代码指令,全局只有一份,所有的类的实例共用一份代码指令;存入到我们的代码段:编译器---代码---》.exe执行文件---》运行这个文件的时候,会把里面的所有代码加载到内存的代码段;

this实例的概念

调用这个成员函数的时候,我们会自动给成员函数,把当前的对象的实例作为this传入进去;t.test30;---> t对象实例[这块内存]---> this,通过this来操作;

类的实例:类的数据成员的所有数据(看得见得,age,4个字节, sex4个字节,string 引用类型变量(4字节), 32为系统下)
看不见得数据:编译器对齐;

组成了一个内得实例; t-->一块内存,写好代码以后编译器会定死一个偏移,age,在我对象实例里面,内存偏移多少,大小为age类型
当我们编写好一个类型以后,
(1)我们得编译器会知道每个数据的相对于对象实例内存块的偏移;

(2)我们编译器也会知道,每个类的成员函数在代码段偏移位置;-->运行的时候,就可以让指令直接跳转到这里,函数调用;

什么是反射,反射有什么作用;

 一:什么是反射
C#编写的程序会编译成一个程序集(.DLL或.exe),其中会包含元数据、编译代码和资源,通过反射可以获取到程序集中的信息

程序集中包含有Microsoft 中间语言 (MSIL) 和必需的元数据。
元数据存储以下信息:

程序集的说明:标识(名称、版本、区域性、公钥)、导出的类型、该程序集所依赖的其他程序集、运行所需的安全权限。
类型的说明:名称、可见性、基类和实现的接口、成员(方法、字段、属性、事件、嵌套的类型)。
特性:修饰类型和成员的其他说明性元素。
区分 反射 与 反编译,反射读取的是元数据,反编译读取的IL代码

二:反射的优缺点
首先在编译中分为动态编译和静态编译,静态编译是在编译中确定类型,绑定对象,而动态编译是在运行中确定类型,绑定对象
反射的优点就是可以动态创建对象、绑定对象,提高了程序的灵活性和扩展性,但反射是一种解释操作,在性能上不如静态编译快

Unity游戏引擎为例,来看反射反射的作用;Unity:编辑器上挂脚本,我们给脚本初始化数据;编辑完了以后---》保存到场景文件里面;
运行的时候,我们就根据场景文件里面的内容,游戏引擎把这个节点和组件实例new出来;

上面我们描述一个类,每一个类是一种类型,都有自己独立的描述;所以我们新加一个类,就会有多的一种方式来描述;
我们办法用统一的方式来处理不同的类或类的实例;
要解决上述我们问题的本质矛盾是什么?
需要用一种方式来描述任意的类;
a:类的实例是一个内存块,内存块的大小就是这个类的所有数据成员的大小--->类的实例的内存块大小;

b:类有哪些数据成员,我可以把这些数据成员的名字,通过数组等其它方式保存起来;
数据成员数组;
{"name", type string,在对象偏移8个字节}{"age" , type int,在对象里面偏移为0个字节}{ "sex", type int,在对象里面偏移为4个字节}

c:类有哪些成员函数
"test", type成员函数(静态函数),在代码段的位置.... }{"test2", type成员函数,在代码段的位置}
{"test3", type成员函数,在代码段的位置}
定义了这样一种描述方式,我们就解决上面的问题,我们用统一的方式来描述任意不同的类型;

每个类,我们的编译器都知道数据成员的偏移,函数代码段的位置,
运行的时候,我们的C#系统会为我们每个类----》描述实例(数据内存);Type类型,Type实例,属于System名字空间;
Type:一些类型的描述信息
class FiledData {
string filedName;int type;//类型
int filedSize;//这个字段的内存大小;int offset;//在内存对象中的内存偏移

string methName;
int type;//静态的还是,普通的;int offset; //函数代码指令的地址;
class Type {
int memSize;l/当前类的实例的内存大小;
List<FiledData> datas;//当前这个类的数据成员;
List<MethodData> funcs;l/当前这个类的所有的成员函数:

我要描述MyTest;
Type t = new Type();t.addFiled("age" ,0);t.addFiled("sex" , O);t.addFiled("name","");
t.addMethod("test",成员方法,地址);t.addMethod("test",成员方法,地址);
编译完成了以后,我们就可以根据我们的编译信息,来为每个类,来生成一个类型描述对象的数据;写入到我们的.exe;

我们的就可以使用了Type的方式来获得一个类的描述;
底层就可以根据类的描述来构建实例,调用方法,和成员了;
调用底层OS的API来分配一个xxxx大小的内存出来;作为对象实例的内存;
调用构造函数,将这个内存块传递给构造函数,构造函数就会帮我们初始化对应的数据;


1:编译每个类的时候,我们会为每个类生成一个全局数据,这个全局数据Type类型,里面存放一个类的描述数据;APl System.Type.GetType("类型的名字”)typeof(T)根据类型或类型名字来获取我们的类型描述对象实例;

2: Type类型系统已经给我们定义好了;
FieldsInfos:数据成员信息;
MethodInfos;
3:通过反射来实例化一个对象:APl: Type t --->实例化一个对象出来(因为知道构造函数的地址直接调用构造函数); new MyTest();

Activator.Createlnstance内存块

4:我们Type里面存放了每个数据成员的偏移,和大小,所以用这两个数据,就能从对象的内存里面读取/设置成员的数据


(1) t--->类型的描述Fieldlnfo,大小,偏移;
(2)结合这个实例,【偏移,大小】---》取出来就是数据的值了; --->SetValue/GetValue;(5):每个Type里面都存放了我们成员函数地址;
methodInfo = t.getMethod("名字");
Object returnObject = methodInfo.Invoke(instance,参数列表);

反射的实例

Unity中创建一个脚本AAA.cs
打开脚本,这时候类名应该也是AAA
写入
public int a = 1;
public string bbb = “111”;
private int c = 2;
然后将脚本拖动到一个gameObject上面
会发现unity为其生成了一些文本框

为什么你在脚本里写了public int age ,在unity里却会显示出一个age框实时显示?代码是开发者自己写的,每个人都不一样,unity为什么会知道你代码里写的什么并显示出来?
因为反射,你在写完脚本后,返回unity,发现unity在转圈,其实就可以理解为在将你的代码编译成程序集,unity通过脚本名字去在程序集中寻找这个类(有种情况就是脚本名如果不跟类名一样会报错!就是因为反射,程序在程序集中找不到脚本名一样的类),找到后会将公共字段遍历显示出来(反射找不到私有片段的),,这个过程就是反射的实例 ,然后你可以在面板这里去填写字段,运行的时候,就相当于我通过反射给程序集(也就是脚本)中的变量去赋值
像脚本前面加上[serialized],就会给私有字段进行序列化,就可以进行反射,所以在面板中就会显示出来
公共字段前面加上[HideInInspector]就不会再面板显示,因为会在反射加载的时候检测,检测到这个字段就不显示了

结构体和类可以,字典加上也不可以


原文链接:https://blog.csdn.net/qq_32175379/article/details/113880100

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值