unity中c#的底层原理

Posted 热心市民亮仔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unity中c#的底层原理相关的知识,希望对你有一定的参考价值。

unity底层运行C#有两种机制:Mono和IL2CPP

1.Mono

C#或者VB这样遵循CLI规范的高级语言,会先被各自的编译器编译成中间语言:IL(CIL),等到需要真正执行的时候,这些IL会被加载到运行时库(CLR),也就是VM中,由VM动态的编译成汇编代码(JIT)然后再执行。

使用Mono的时候,脚本的编译运行如下图所示:

3大脚本被编译成IL,在游戏运行的时候,IL和项目里其他第三方兼容的DLL一起,放入Mono VM虚拟机,由虚拟机解析成机器码,并且执行。

2.IL2CPP

IL2CPP主要由两部分组成:

1.AOT静态编译编译器(il2cpp.exe),将IL转换为C++源码,再交给各平台的C++编译器进行编译,达到平台兼容的目的

2.运行时库(libil2cpp),运行时库则会提供诸如垃圾回收、线程/文件获取、内部调用直接修改托管数据结构的原生代的服务与抽象

使用IL2CPP的时候,脚本的编译运行如下图所示:

在Unity编译打包时,IL2CPP模式同样先将工程中的文件编译为IL,但是IL2CPP不会直接在虚拟机中运行这些IL,而是将工程IL和其他IL一起使用IL2CPP将这些IL变回CPP(C Plus Plus或C++)代码,然后再将C++代码放到本地编译器(具体运行的机器平台的编译器)上编译为能执行的原生汇编代码,再使用自研IL2CPP VM运行。

3.特点对比

优点

缺点

Mono

1.构建应用非常快

2.由于Mono的JIT(Just In Time compilation ) 机制, 所以支持更多托管类库

3.支持运行时代码执行,安卓上dll动态载入程序集热更新

4.必须将代码发布成托管程序集(.dll文件,由mono或者.net生成)

1.维护困难、版权限制,而低版本的mono就无法使用C#的强大特性;
2.mono需要运行在虚拟机内,相比于编译成原生的CPP代码而言,效率非常低

IL2CPP

1.相比Mono, 代码生成有很大的提高

2.可以调试生成的C ++代码

3.程序的运行效率比Mono高,运行速度快

4.可以启用引擎代码剥离(Engine code stripping)来减少代码的大小

5.多平台移植非常方便

1.只支持AOT(Ahead of Time)编译

2.相比Mono构建应用慢

IL2CPP比较适合开发和发布项目 ,但是为了提高版本迭代速度,可以在开发期间切换到Mono模式(构建应用快)。

unity中C#反射的底层原理

五一节要劳逸结合,玩了一天晚上也要学习~毕设也忙完了,最近应该会开始更新博客了。
记录一下今晚公开课学到的unity中c#的底层原理
先看一看反射的概念
在程序运行时,动态获取 程序集, 类型(class,interface)和类型的成员信息(方法,字段,属性等)。
在程序运行时,动态创建 类型实例, 以及调用和方法 动态创建出来的 类型实例的成员。

首先我们需要明白内存机制,四大内存空间(堆、栈、全局变量、代码数据);借由类的生成来记录一下吧,类是程序员自己创建的所以自然内存是在堆内,而类当中的方法和非全局变量却不是在堆内的,而是存储在代码数据块内的。

Mycalss t = new Myclass();
t.equalnum(1,2,"3")
public void equalnum(int a,int b,string c)

    this.a = a;  this.b = b;  this.c = c;

调用equalnum这个成员函数的时候,系统会自动把当前对象的实例作为this指针指向的对象。 t==》对象实例[即一段内存地址] ==》this,this指向该段地址。
那么接下来我们再思考一下,我们写好的类挂载到unity引擎上面,并给脚本初始化数据;然后就是保存到场景文件中。当我们运行程序的时候,我们就会根据场景文件里的内容,游戏引擎把这个节点和组件new出来。

class Myclass1;
class Myclass2;//假如有多个类挂载到程序中

//引擎底层运行时就需要判断类名是否一致然后才确定运行某个组件
if(ClassName == "Myclass1")
------执行class1的逻辑
else if(ClassName == "Myclass2")
-----执行class2的逻辑
else if......//等等

这样虽然能够运行正确且我们容易理解,但是对于引擎底层和执行来说如果是海量的组件和类这样会不会效率低了很多而且麻烦。那么就引起我们的思考有没有一种方法:我们采用一个统一的方式来处理所有不同的类或类的实例呢? 即所有任意的类,都可以转化为一种描述!
1.类的实例本质是一个内存块,这个内存块的大小即代表了这个类中所有的数据内存总和大小(即描述内存地址和大小)
2.类中有哪些数据成员,我们可以把数据成员的名字,通过数组列表等方法保存起来 (即描述数据成员的名字)
例如我们第一个类MyClass有三个成员变量一个成员方法,那么依照刚刚的方法


 "a" : type:int ,在类中地址 内存为4个字节
 "b" : type:int ,在类中地址  内存为4个字节
 "c" : type:string,在类中地址  内存为8个字节
 "equalnum" : type:成员函数 , 在代码数据块的位置

这样不管我们的类有多少方法和变量都可以统一的格式去描述了。
3.类型描述:
对象实例(Type) 既然我们类中的信息都描述了,那么往上一层我们也应该记录类名的信息和类型,也应该用同样的思想去描述:
每一个类,我们的编译器都知道,数据成员在代码数据块的内存地址和内存大小;运行的时候,c#系统会为我们每一个类描述实例(即记录内存信息);Type类型,Type实例属于System命名空间

class FiledData 
    string filedName;
    int type; //类型
    int filedSize; //这个字段的内存大小;
    int offset; //在内存对象中的内存偏移;

class MethodData 
    string methName;
    int type; //静态的还是,普通的;
    int offset; //函数代码指令的地址;

class Type 
    int memSize; //当前类的实例的内存大小;
    List< FiledData> datas; //当前这个类的数据成员;
    List < MethodData> funcs; //当前这个类的所有的成员函数;


到这里我们基本就从成员数据到类的描述都构思好了,那么接下来继续用第一个类Myclass来举例子看看底层的描述

Myclass t = new Myclass()
t.addFiled("a",1);
t.addFiled("b",2);
t.addFiled("c","3");
t.addMethod("equalnum",成员方法,地址);

底层编译完之后,我们就可以根据我们编译的信息,来为每一个类,生成对其描述的数据存储起来,写入到.exe中(即代码数据块内存的最终去向)
这样我们就可以直接通过Type来获得任何一个类的描述信息了。引擎底层也就不需要一直用if判断是否类名一致,直接就可以根据类的描述信息来构造实例,并调用其方法和成员了。
调用底层OS的API来分配一个一定大小的内存空间,作为对象实例的内存;然后调用构造函数,将这个内存传递给构造函数,构造函数就能够精准的将数据初始化了。
那么把上面的全部理解之后,就可以很轻松的消化反射了(好像都快忘了我是在学习反射了),我们现在加上反射的思想把逻辑梳理一遍
(1): 编译每个类的时候,我们会为每个类生成一个全局数据,这个全局数据Type类型,里面存放一个类的描述数据;
API System.Type.GetType("类型的名字”) typeof(T) 根据类型或类型名字来获取我们的类型描述对象实例;
(2):Type类型系统已经给我们定义好了;
FieldsInfos:数据成员信息;
MethodInfos;
(3):通过反射来实例化一个对象:
API:Type t -->实例化一个对象出来;
new Myclass();
Activator.Createlnstance
(4):我们Type里面存放了每个数据成员的内存地址,和内存大小,所以用这两个数据,就能从对象的内存里面读取/设置成员的数据
(1) t–>类型的描述FieldInfo,大小,偏移;
(2)结合这个实例,[内存地址, 内存大小] —》 取出来就是数据的值了; --> SetValue/GetValue;
(5):每个Type里面都存放了我们成员函数地址;
methodInfo = t.getMethod(“名字”);
Object returnObject = methodInfo.Invoke(instance,参数列表);

我们再返回头来看看反射的概念就很好理解了
在程序运行时,动态获取 程序集, 类型(class,interface)和类型的成员信息(方法,字段,属性等)。
在程序运行时,动态创建 类型实例, 以及调用和方法 动态创建出来的 类型实例的成员。

教程链接:https://www.bilibili.com/video/BV1x44y1J7ok?spm_id_from=333.337.top_right_bar_window_custom_collection.content.click

以上是关于unity中c#的底层原理的主要内容,如果未能解决你的问题,请参考以下文章

Unity内存管理你应该知道的底层原理

Unity内存管理你应该知道的底层原理

Unity内存管理你应该知道的底层原理

unity中GetComponent获取组件中的材质的颜色,用C#写

Unity 游戏用XLua的HotFix实现热更原理揭秘

[Unity] C#中级编程 - 09 - 扩展方法