找回密码
 立即注册
查看: 226|回复: 0

Unity中C#的底层原理

[复制链接]
发表于 2022-12-16 10:36 | 显示全部楼层 |阅读模式
这一部分涉及到C#的底层运行原理,主要是一些概念的理解,来迎接新名词的暴击!
知识点涵盖:Mono、IL2CPP、CLR、托管代码&非托管代码、反射
1 底层运行机制

Unity早期支持Boo、UnityScript(无限接近于JavaScripts)、Boo。Unity在底层运行C#代码的时候有两种机制:Mono、IL2CPP。
1.1 Mono

这些语言需要被各自的编译器编译成IL/CIL,再由Mono虚拟机进行解释和执行。
IL,Intermediate Language, 中间语言,CIL特指.net平台下的IL标准,两者是同义词。这是一种低阶的人类可读的语言,可以被看作是一个面向对象的汇编语言,完全基于堆栈,必须运行在虚拟机上。


IL有三种转译模式:
JIT:Just-In-Time,程序运行过程中进行转译;
AOT:Ahead-Of-Time,提前将IL转译成机器码并且存储在文件中,这个文件不能完全独立运行。
完全静态编译:只支持少数平台,基于AOT进一步生成所有的机器码。
ios使用了最后一种,安卓和windows使用JIT。
Mono核心包含三类组件:
核心组件:C#编译器、Common Language Infrastructure虚拟机、以及核心类别程序库;
开发堆栈:提供了用于开发应用软件的工具;
微软兼容堆栈:使得.net应用程序可以被移植到 GNU/Linux上。
Mono中的垃圾回收器:3.1.1之后的版本默认的垃圾回收器是SGen-GC,采用了分代垃圾回收的思想。
Mono的缺陷:维护成本大;版本授权受限;运行效率不如IL2CPP。
1.2 IL2CPP

IL2CPP在得到IL之后将其翻译成C++,再由C++编译器直接编译成能够执行的机器码。
IL2CPP也有自己的虚拟机,主要用于内存管理,和Mono的内存管理类似。


2 CLR:Common Language Runtime

中文译名叫公共语言运行时,能够运行代码并且提供相应的服务。
两个功能:提供运行环境+提供相应的服务
其中相应的服务目前我们会涉及到的不多,需要知道这些服务中包括了GC。其他的服务如下:
Performance improvements.
The ability to easily use components developed in other languages.
Extensible types provided by a class library.
Language features such as inheritance, interfaces, and overloading for object-oriented programming.
Support for explicit free threading that allows creation of multithreaded and scalable applications.
Support for structured exception handling.
Support for custom attributes.
Garbage collection.
Use of delegates instead of function pointers for increased type safety and security. For more information about delegates, see Common Type System.
C#的底层编译过程是:编译器将代码编译成CLR能理解的格式,CLR运行代码。其中元数据的格式、运行虚拟机、类型系统都遵循ECMA标准。
运行在CLR中的代码叫做托管代码(Managed Code), 不运行在这个环境中的叫做非托管代码。这两者之间的区别如下表所示:
托管代码非托管代码
内涵CLR控制下的代码不运行在CLR下的代码
外延Unity中的C#代码;
由虚拟机进行编译和执行;
C/C++代码;
虚拟机无法跟踪到这类代码;
适用场景编写游戏逻辑更加底层的架构、第三方库、操作系统等相关接口
优势CLR提供了一系列的托管服务需要程序员自己进行管理,否则会造成运行错误或者内存泄漏。
3 程序集 & 反射

3.1 程序集

程序集:assembly,是.net应用的基础组成单位,表现为exe、dll文件。每一个程序集只有一个入口。程序集分为静态和动态,静态的程序集存储在硬盘上的PE文件中,动态的程序集直接从内存中加载运行,运行之后可以保存到磁盘中。
元数据:为了CLR能够向托管代码提供服务,语言编译器必须产出元数据(描述类型、成员、引用的文件),每一个PE文件(可移植、可执行文件,表现为exe、dll等格式的文件)都需要包含元数据。CLR需要元数据来生成机器码。
3.2 反射

反射是通过审查元数据并在.net中获取运行时信息的方式,这些信息包括描述程序集、模块和类型的对象(Type类型)。也可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后调用其方法或访问器字段和属性。 如果代码中使用了特性,可以利用反射来访问它们。
首先在编译中分为动态编译和静态编译,静态编译是在编译中确定类型,绑定对象,而动态编译是在运行中确定类型,绑定对象。反射的优点就是可以动态创建对象、绑定对象,提高了程序的灵活性和扩展性,但反射是一种解释操作,在性能上不如静态编译快。
可以做到的事情:
(1)通过Type类获取程序集、模块、类的信息
得到一个Type类型对象有三种方法:object.GetType()、Type.GetType()、typeof()
    int[] a = { 42, 34, 1, 2 };
    // Start is called before the first frame update
    void Start()
    {
        Type type = a.GetType();
        Debug.Log(type);
        Debug.Log("Name: " + type.Name);
        Debug.Log("Assembly: " + type.Assembly);
        Debug.Log("BaseType: " + type.BaseType);
        Debug.Log("Is class? " + type.IsClass);
    }



获取属性信息

(2)动态创建实例:这种方式比new效率要低
public class Reflection : MonoBehaviour
{
    int[] a = { 42, 34, 1, 2 };
    // Start is called before the first frame update
    void Start()
    {
     //创建一个类的实例
        object c = Activator.CreateInstance(typeof(TestClass), "hello world");
        Debug.Log(c.ToString());

//创建一个结构体的实例
        student stua = (student)Activator.CreateInstance(typeof(student));
        stua.name = "kk";
        stua.age = 12;
        Debug.Log("The boy " + stua.name + " is " + stua.age + " years old.");
    }

public class TestClass
{
    public string str;
    public TestClass(string str)
    {
        this.str = str;
    }

}

struct student
{
    internal string name;
    internal int age;
    public void ShowName()
    {
        Debug.Log(name);
    }
}

(3)获取方法或者动态调用方法
public class Reflection : MonoBehaviour
{
    int[] a = { 42, 34, 1, 2 };
    void Start()
    {
        object c = Activator.CreateInstance(typeof(TestClass), "hello world");
        Debug.Log(c.ToString());
        var m1 = c.GetType().GetMembers();
        var m2 = c.GetType().GetMethod("ShowName");
        m2.Invoke(c, null);
    }
}

public class TestClass
{
    public string str;
    public TestClass(string str)
    {
        this.str = str;
    }
    public void ShowName()
    {
        Debug.Log("this is a test class");
    }
}

参考文献:
C#中的反射_Hello Bug.的博客-CSDN博客_c# 反射
Assemblies in .NET
《Unity3D高级编程主程手记》

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-15 22:49 , Processed in 0.089469 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表