《CLR via C#》笔记:第2部分 设计类型(1)

  1.7 C#, 3.2 游戏引擎技术
  • 本博客所总结书籍为《CLR via C#(第4版)》清华大学出版社,2021年11月第11次印刷(如果是旧版书籍或者pdf可能会出现书页对不上的情况)
  • 你可以理解为本博客为该书的精简子集,给正在学习中的人提供一个“glance”,以及对于部分专业术语或知识点给出解释/博客链接。
  • 【本博客有如下定义“Px x”,第一个代表书中的页数,第二个代表大致内容从本页第几段开始。(如果有last+x代表倒数第几段,last代表最后一段)】
  • 电子书可以在博客首页的文档-资源归档中找到,或者点击:传送门自行查找。如有能力请支持正版。(很推荐放在竖屏上阅读本电子书,这多是一件美事)

该部分知识点十分重要,但如果不经过实际工程操作,可能无法有效的深入理解

第四章 基础类型

所有类型都从System.Object派生

  • Runtime要求每个类型最终都从System.Object类型派生,所以class A等价于class A:System.object。(P81 2)
image 36 - 《CLR via C#》笔记:第2部分 设计类型(1)
image 37 - 《CLR via C#》笔记:第2部分 设计类型(1)
image 38 - 《CLR via C#》笔记:第2部分 设计类型(1)
  • CLR要求所有对象都用new操作度创建,操作完成后返回引用(或指针),且不能被delete主动回收,系统会自动检测回收。new操作符所做的事情:(P82 last2)
    1、计算类型及所有基类型中定义的所有实例字段需要的字节数。
    2、从托管堆中分配类型要求的字节数,从而分配对象的内存,分配的所有字节都设为0。
    3、初始化对象的“类型对象指针”和“同步块索引”成员
    4、调用类型的实例构造器,传递在new调用中指定的实参。

类型转换

  • C#不需要任何特殊语法即可将对象转换为它的任何基类型(该转换被认为是安全隐式转换)(P83 4)CLR不允许类型伪装,他会造成安全漏洞以及破坏应用程序的稳定性和健壮性。类型安全是CLR极其重要的一个特点。(P84 last)
  • 扩展-什么是隐式转换和显式转换:传送门
//基类型和派生类型的转换
internal class Employee{}

public sealed class Program
{
        public static void Main()
        {
                //不需要转型,Object是Employee的基类型
                Object o = new Employee();
                //需要转型,因为Employee派生自Object
                 Employee e = (Employee)o;
        }
}
  • 使用is和as操作符转型:(P85 1)
//is类型:CLR首先判断变量引用的对象实际类型,然后遍历继承层次结构,用每个基类型核对指定类型。这样会耗费更多时间。
//如果对象引用null,is操作符总是返回false,因为没有可检查其类型的对象
object o = new Object ( ) ;
Boolean bl = (o is Object) ;//b1 为true,is会核实o是否兼容于该类型
Boolean b2 = (o is Employee) ;//b2为false.

//as类型:工作方式与强制转换类型一致,但永远不抛出异常,如果对象不能转型则结果为null
object o = new Object ( ) ;
Employee e = o as Employee;//转型失败,值为null

命名空间和程序集

  • 命名空间对相关的类型进行逻辑分组,开发人员可通过命名空间方便地定位类型(使用using指令访问空间,相信各位都比较熟悉了)。(P86 last2)
  • 如果有多个命名空间出现同一方法语句,则必须显示调用改方法,以区分不同空间。(P88 1)
image 39 - 《CLR via C#》笔记:第2部分 设计类型(1)
  • 命名空间和程序集的关系:不一定相关,同一个命名空间的类型可能在不通程序集中实现,同一个程序集也可能包含不同命名空间中的类型。(P89 last)

运行时的相互关系(重要底层原理)

  • 类型、对象、线程栈和托管堆在运行时的相互关系。
    调用静态方法、实例方法和虚方法的区别。
  • 现有M1方法,M1中调用M2方法。(P90 1,强烈建议计算机系统没学好的去原文补一下)
    1、系统会先把M1压入栈执行操作
    2、在M2方法前先压入返回M1的地址,然后执行调用M2方法时压入栈,
    3、M2方法结束之时通过返回地址返回
  • 假设有如下代码:
void M3()
{
        Employee e;
        lnt32 year;
        e= new Manager():
        e = Employee.Lookup("Joe");
        year = e.GetYearsEmployed();
        e.GetProgressReport():
}
image 40 - 《CLR via C#》笔记:第2部分 设计类型(1)
  • JIT编译器(Just-In-Time Compiler,即时编译器)执行时:(P92 last)
    1、将M3的IL代码转换成本机CPU指令时,并确保能招到M3所有定义这些类型的相关程序集都已加载
    2、CLR利用程序集元数据,提取与这些类型有关的信息,创建一些数据结构来表示类型本身
    3、CLR确认程序集完毕后,将所有局部变量初始化为null或0(对应代码函数中1、2行)
    4、M3执行代码构造一个Manager对象,托管堆中创建Manager类型的一个实例(所有对象都有类型对象指针和同步块索引)
    5、CLR初始化同步块索引,并将对象所有实力字段设置为null或0,然后调用类型的构造器,new操作符返回Manager对象的内存地址(函数第3行)
    6、调用静态方法Lookup:CLR定位于定义静态方法的类型对应的类型对象,JIT编译器在类型对象的方法表中查找被调用方法对应的记录项,对方法进行JIT编译(如需要)。Lookup在方法堆上构造新的Manager对象(第3行构造了第1个)然后用Joe初始化并返回对象地址给e。(函数第4行)
    7、调用Employee非虚实例方法GetYearsEmployed:如果Employee类型中没有定义正在调用的方法,JIT编译器会回溯类层次结构,并在沿途的每个类型中查找该方法(此时只用Lookup被定义过)。(函数第5行)
    8、调用Employee虚实例方法GetProgressReport:JIT编译器在方法中生成一些额外代码,方法每次调用都会执行,代码首先检查发出调用的变量,并跟随地址来到发出调用的对象。然后检查对象内部“类型对象指针”成员(指向对象实际类型)。然后再类型对象的方法表中查找引用了被调用方法的记录项,对方法进行JIT编译(如需要),再调用JIT编译好的代码。
image 41 - 《CLR via C#》笔记:第2部分 设计类型(1)

LEAVE A COMMENT