理解C#核心概念–C#模块(module)

理解C#核⼼概念–C#模块(module)
模块,准确的来说,并不是C#的概念,⽽是微软的运⾏时环境CLR的概念。想要学好C#断然不能只是会使⽤VS之类的IDE写写代码然后编译运⾏,很多相关的概念我们也需要去了解。
渗透压力这⼀节,我们就简单讨论下C#模块。
其实C#模块,我们⼏乎每天都需要接触到(如果我们每天都需要写C#代码并且进⾏编译的话),每次我们对⼀个C#类(class)进⾏编译都会⽣成模块。
csc编译获得模块
下⾯,我们以经典的hello world为例。
昂达v7111. 创建⼀个Program.cs⽂件,内容只是简单的向控制台输出hello world:
桑乐屋public sealed class Program{
public static void Main() {
System.Console.WriteLine("hello world");
}
}
1. 使⽤ 对⽂件进⾏编译:
< / /t:exe /r:MSCorLib.dll Program.cs
如果以上命令运⾏成功的话,我们会在源⽂件的⽂件夹中得到⼀个的⽂件。这个 就是⼀个标准的PE32或者PE32+的⽂件(PE32+对应的是64位,这⾥没有PE64),也就是⼀个C#模块。
在继续讲解C#模块之前, 我们先来看看上⾯这个编译命令,这个命令还是有点意思的。
/ 告诉了编译器我们最后需要⽣成的是⼀个exe⽂件。
/t:exe 看起来和上⼀个out参数似乎有些重复,这也是这个参数⽐较迷惑⼈的地⽅,t其实代表的是target,exe其实只是指代了没有图形界⾯的应⽤程序。如果是我们平常使⽤的⽐如迅雷之类的应⽤那么使⽤的就应该是winexe。这种类型是具有图形界⾯的。
/r:MSCorLib.dll,r这⾥是reference,是为了声明编译Program.cs我们需要使⽤到定义在MSCorLib.dll 中的某些类型(在我们的例⼦中就是Console)。
新一代的洗衣粉Response⽂件
有⼼的朋友可能会发现,其实我们并不需要声明MSCorLib.dll的引⽤,直接使⽤如下命令,也能够编译成功。
< / /t:exe Program.cs
事实上确实如此,为了⽅便进⾏编译,⽽不需要每次都输⼊⼀串参数,csc允许我们使⽤Response⽂件来提供参数。
因此我们可以把刚刚使⽤到的三个参数全部放到response ⽂件中并保存为Program.rsp,这个⽂件内容如下:
/out:
/t:exe
/r:MSCorLib.dll
然后运⾏如下命令,也可以得到同样的结果。
< @Program.rsp Program.cs
参数覆盖规则
聪明的朋友肯定⼜发现了,可是我们之前并没有定义response⽂件,为什么我们还是可以不指定引⽤。这个原因就在于,csc⾃带了⼀个全局默认的response⽂件。我们可以在csc的安装⽬录中到这个csc.rsp⽂件。⽽这个csc.rsp⽂件就包含了许多系统⾃带的常⽤的引⽤。因此我们并不需要去声明这些引⽤。
⽼⽩使⽤的是VS2019安装的版本,因此⽂件可以在“C:\Program Files (x86)\Microsoft Visual
Studio\2019\Community\MSBuild\Current\Bin\Roslyn\”路径中到,同样的,这个⽂件夹也包含了csc.rsp⽂件。
既然存在多个地⽅可以进⾏配置,那必然会存在优先级的问题。想必聪明的朋友已经想到了,csc采⽤的是以下顺序获取参数,⼀旦获得需要的参数就不再向下查:
1. 命令⾏中带的参数
2. 本地传⼊命令⾏的response⽂件中的参数
3. 全局的csc.rsp中的参数
模块内容
通过csc编译出来的模块,需要使⽤CLR进⾏运⾏。那么⼀个模块是怎么让CLR知道应该怎么进⾏处理的呢?这就需要我们看看模块到底包含了哪些信息了。
PE32/PE32+的头⽂件
头⽂件这个东西,名字很有意思,其实也⾮常准确,⼀般来说我们看到⼀个⼈的“头”就能知道这个⼈是谁(who),⼤致的年龄(how old,简称how),虽然不准确但是灵魂三问已经完成两问了。
同样的,PE32/PE32+头⽂件也给我们提供了这些信息。头⽂件⾥包含了PE⽂件类型的信息(who),
我们可以知道这是⼀个GUI⽂件还是⼀个普通DLL等等。此外也包含了⽂件创建信息(how old)。如果这个PE头⽂件使⽤了PE32的格式,那么就可以在32位或者64位的Windows上运⾏。如果使⽤的是PE32+的格式,那么只能在64位的Windows上运⾏。
CLR 头⽂件
如果说PE32/PE32+头⽂件没什么⽤途的话(事实上也确实如此,CLR⼤部分情况下并不使⽤PE头⽂件),那么CLR头⽂件则是包含了CLR运⾏此⽂件的必要信息。⼏个主要的信息包括了,所需要的CLR版本,模块的⼊⼝函数,模块的metadata数据位置和⼤⼩等等。
元数据(metadata)
tm2007
主要包含了两部分,⼀部分是这个模块本⾝所包含的类型(type)和成员(member)。另外⼀部分描述了这个模块会引⽤到类型和成员信息。
硅酸盐学报IL 代码
整个模块的核⼼部分,是编译器编译C#源码所⽣成的中间代码。在运⾏时,IL代码会被CLR编译成cpu指令。
这就是⼀个模块所包含的4个部分。这⾥⽐较重要的⼀点是这四个部分都是相互耦合在⼀起的,这样就能够保证元数据和IL代码之间不会出现不同步的问题。
此外,可能有很多同学都会有困惑,为啥已经有了IL代码了,其实就能够知道这个模块本⾝所包含的类型和成员也能之后引⽤了哪些其他模块,为啥还需要元数据呢?我的理解也不⼀定准确,但是很⼤⼀个作⽤就是GC可以通过阅读元数据来很快的判断哪些类型之间有引⽤,从⽽判断是否需要进⾏GC。此外,我们编写的源码,在进⾏编译的时候,编译器也能够很快的通过元数据来判断我们的源码是否合规。
好了,到此,⽼⽩就将如何(⼿动)产⽣⼀个模块以及模块的组成说完了。如果有什么问题,欢迎⼤家和⽼⽩⼀起探讨。
码字不易,如果觉得有⽤或者喜欢⽼⽩的内容,欢迎点赞收藏关注。

本文发布于:2024-09-20 16:31:26,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/648656.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:模块   参数   需要   编译   包含   部分   命令
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议