C#是部分解释还是真正编译?


74

关于此有很多矛盾的信息。尽管有些人说C#已被编译(因为它被编译为IL,然后在运行时被编译为本机代码),但其他人却说它被解释为需要.NET。EN Wiki说:

首先将许多解释语言编译为某种形式的虚拟机代码,然后在运行时将其解释为或编译为本机代码。

所以我很困惑。谁能解释清楚吗?




inb4有人发明了一种以LLVM IR分发的语言,并要求用户安装一些独立的LLVM实例。
SOFe

Answers:


74

C#由c#编译器编译为IL。

然后,根据需要将此IL及时进行编译(JIT),将其编译为主机的本机汇编语言。不过,可以编写一个.NET运行时来解释IL。即使这样做,我仍然会认为c#是一种编译语言。


4
但是为什么许多程序员都认为它是解释语言?
约翰五世

16
就个人而言,我从未听说过有人将C#称为解释性语言。
jrummell 2012年

8
也许他们只是被误解了,或者被误解了。也许我错了:-)
西蒙(Simon)

21
JIT编译!=解释
支出者2012年

3
@JoSmo csc.exe是生成c#的命令行工具。它代表C-Sharp编译器
Simon

43

纯编译语言具有一些优势。通常,速度通常是工作集的大小。纯解释语言具有一些优势。不需要显式的编译阶段即可使我们进行就地编辑的灵活性,并且通常更容易移植。

在这种情况下,中间语言适合使用中间语言。

仅仅是出于这个原因,我们可能会认为拼凑的语言是编译的还是解释的,这取决于我们关心达到哪个度量标准的位置以及我们对一种或另一种的偏见。

C#也可以像ASP.NET中那样在第一次运行时进行编译,这使得它在这种情况下几乎可以解释(尽管在这种情况下,它仍然可以编译为IL,然后启动)。当然,在这种情况下,它具有解释器的几乎所有优点(与经典ASP中使用的VBScript或JScript相比),以及编译的许多优点。

严格来说,任何语言都不能被固定,解释或编译。我们可以将NGen C#转换为本机代码(尽管如果执行诸如动态加载程序集之类的操作,它仍将使用IL和jitting)。我们可以为C或C ++写一个解释器(有很多人这样做)。不过,在最常见的用例中,C#会编译为IL,然后将其初始化,这既不是解释型还是编译型的经典定义。


我同意no language is jitted, interpretted or compiled qua language。我看到了一些有关python的信息,它首先被编译为Python,byteCode然后在运行时byteCode由相应OS的解释器进行解释。由于编译,精简和解释语言的定义,这一事实令我惊讶不已。
RBT

JIT语言可以比本地语言更快。您知道gcc具备的仪器功能,可以在运行时输出性能测量文件,然后将其反馈给gcc以编译更优化的二进制文件吗?JVM和CLR都连续地这样做。
约翰·摩瑟

18

基于观点的语义和陈述过多。

首先,C#不是一种解释性语言。CLR和JVM被视为“运行时”或“中间件”,但相同的名称适用于Perl之类的东西。这在与名字有关的人们之间造成了很多混乱。

引用运行时的术语“解释器”通常表示现有代码解释某些非本机代码。有两个大的范例:解析读取原始源代码并采取逻辑操作;字节码执行首先将代码编译为非本机二进制表示形式,这需要更少的CPU周期来解释。

Java最初被编译为字节码,然后经过一个解释器。现在,JVM读取字节码并将其及时编译为本地代码。CIL的作用相同:CLR使用即时编译本机代码的功能。

考虑正在运行的源代码,正在运行的字节码,编译为本机,及时编译,通过编译器到实时本机运行源代码的所有组合,等等。语言是编译还是解释的语义变得毫无意义。

例如:许多解释语言都使用即时字节码编译。C#编译为CIL,JIT编译为native;相比之下,Perl立即将脚本编译为字节码,然后通过解释器运行该字节码。您只能以CIL字节码格式运行C#程序集;您只能以原始源代码格式运行Perl脚本。

即时编译器还运行许多外部和内部工具。运行时跟踪各种功能的执行,然后调整代码布局以针对其特定的执行流程优化分支和代码组织。这意味着JIT代码的运行速度比本地编译的代码(通常像C ++或通过IL2CPP运行的C#)要快,因为JIT在运行时会根据代码的实际执行情况调整其优化策略。

欢迎来到计算机编程世界。我们决定使其变得极其复杂,然后将非描述性名称附加到所有内容中。目的是在没有实际意义的单词定义上发动大战。


JVM解释字节码,然后将某些方法/循环体编译为本地代码。该编译可以与字节码解释并行进行,并且代码可以热插拔。
史蒂夫斯,

我找不到理由。我的理解是,JVM在JIT运行时解释字节码-也就是说:当遇到字节码时,JVM在当前运行期间尚未处理,它将通过JIT运行并执行生成的本机代码块。JIT-and-execute比字节码解释便宜,并且实现这两个系统都非常复杂。
约翰·摩瑟

结帐,例如slideshare.net/ZeroTurnaround/…。您要描述的是.NET的功能。我所描述的是例如Hotspot JVM所做的,是的,它非常复杂,这就是为什么它已经开发了一段时间的原因。您需要对代码进行字节码解释以获取配置文件信息(请考虑:如果大部分时间都采用了分支等),而不是在优化编译器中使用的信息(如果从未采用过分支,则不要花费太多)优化资源)。
史蒂夫斯,

1
实际上,perl现在也被忽略了。
Erik Aronesty

您可以将性能分析放入本机代码中。gcc具有此类的分析工具。编译器可以识别循环迭代器,并添加代码以比较其进入和退出状态。可以在分支发生时设置变量;等等。每次JIT检查性能分析数据之间的总次数就是调用总数,循环计数,分支计数等等。它花费几个周期,几乎不比解释字节码大。
约翰·摩瑟

14

如果您觉得,学到的或者是守旧的,那么编译后的EXE正在从源代码转换为机器代码,则将解释C#。如果您认为编译意味着将源代码转换为其他代码(例如字节代码),则可以将其转换。对我来说,任何需要运行时处理才能在为其构​​建的OS中工作的东西都会得到解释。


2
我们不是要提出意见,“对我来说”是一种意见。这不是论坛。
安吉洛·马斯卡洛

5
不幸的是,问题的性质一种观点。
Erik Aronesty

13

在这里查看:http : //msdn.microsoft.com/library/z1zx9t92

用C#编写的源代码被编译成符合CLI规范的中间语言(IL)。

(...)

执行C#程序时,程序集将加载到CLR中,该CLR可能会基于清单中的信息采取各种措施。然后,如果满足安全要求,则CLR会及时执行(JIT)编译,以将IL代码转换为本地机器指令。


谢谢,因此,如果我正确理解的话,与解释器的混淆可能来自所需的虚拟机(CRM),但这不是真正的解释器。
约翰五世

6

首先,让我们了解解释和编译的定义。

“编译”(指代码)是指将代码从一种语言翻译成另一种语言。通常从人类可读的源代码转换为目标处理器可以处理的机器代码。

“解释”(在引用代码时)表示将代码从一种语言翻译成另一种语言。但这一次通常用于将人类可读的源代码转换为中间代码,然后由虚拟机将其转换为机器代码。

只是要清楚
源代码->编译器->机器代码
源代码->编译器->字节代码->解释器->机器代码

理论上,任何语言都可以解释编译。通常,Java被编译为字节码,而字节码由Java虚拟机解释为机器码。通常将C#解释为字节码,该字节码由CLR,公共语言运行库,另一个虚拟机编译。

到目前为止,整个事情都是营销头。添加了“解释的”一词(或至少增加了用法)以帮助展示即时编译的整洁程度。但是他们本可以只使用“编译”。区别更多是对英语和商业趋势的研究,而不是任何技术性质的研究。


7
在解释器中,运行时不生成任何机器代码,仅执行现有的机器代码。按照该标准,将公共语言运行库称为解释器是完全不正确的。
Ben Voigt 2014年

3

C#在其生命周期中都进行了解释和编译。C#被编译为VM解释的虚拟语言。

混淆源于“编译语言”的模糊概念。

从某种意义上说,“编译语言”是用词不当,因为编译或解释不是语言的属性,而是运行时的属性。

例如,您可以编写C解释器,但人们通常称其为“编译语言”,因为C实现可编译为机器代码,并且在设计语言时考虑到了编译。


3
不,桌面.NET VM(称为公共语言运行时)具有JIT编译器,没有解释器。相比之下,在甚至可能不支持运行时代码修改的微型嵌入式设备上发现的.NET Microframework确实可以解释IL。
Ben Voigt 2014年

源->(编译器)->中间语言->(解释器)->本机代码。我将JIT视为解释器优化。它基本上是在解释器层“缓存”。如果愿意,可以调用两个步骤的编译器。
2014年

2

大多数语言(如果不是全部)都需要一个解释器,以将其脚本翻译为机器代码,以使cpu理解并执行它!

每种语言对翻译过程的处理方式都不一样!

例如,“ AutoIt”就是我们可以描述为100%解释的语言!

为什么?

因为执行脚本时经常需要“ AutoIt”解释器!请参见下面的示例:

Loop, 1000
Any-Code

“ AutoIt”解释器将必须将“ Any-Code”转换为机器代码1000次,这会自动使“ AutoIt”成为慢速语言!

另一方面,C#处理翻译过程的方式不同,在脚本执行之前仅需要一次C#解释程序,此后在脚本执行期间就不再需要它了!

C#的解释器只需将“ Any-Code”翻译为机器代码一次,即可自动使“ C#”成为一种快速的语言!

所以基本上

  • 在脚本执行期间需要其解释程序的语言是“解释语言”!

  • 仅需要一次解释器(在脚本执行之前)的语言就是“编译语言”!

最后,

  • “ AutoIt”是一种“解释语言”!

  • “ C#”是一种“编译语言”!


1

我相信这是一个很老的话题。

从我的角度来看,解释后的代码将通过解释器进行逐行转换并同时执行。像示例javascript一样,它是一个解释代码,当一行javascript遇到错误时,脚本就会中断。

在编译代码时,它将经过编译器,将所有代码立即转换为另一种形式的代码,而无需先执行。执行是在另一个上下文中。


JavaScript要么立即像C#一样被JIT编译,要么像与HotSpot JVM一样被执行,直到部分代码变热为止,然后通过优化的编译器对其进行JIT编译。它取决于JavaScript引擎。JavaScript的解释已经超过10年了。
朱利安·延森


0

由于计算机只能执行二进制代码,因此任何一种语言都会导致在某一点或另一点产生二进制代码。问题是:该语言是否允许您以二进制代码生成程序?如果是,则它是一种编译语言:按照定义,“编译语言”中的“编译”是指编译为二进制代码,而不是转换为某些中间代码。如果该语言导致为程序生成这样的中间代码,则它将需要其他软件才能从该代码执行二进制编译:那么它就是一种解释型语言。由C#“编译”的程序是否可以直接在计算机上执行而无需在该计算机上安装任何其他软件?如果否,则为解释性语言。对于翻译语言,它是一个解释器,大多数时候将以动态方式生成底层的二进制代码,因为这种机制是此类语言灵活性的基础。雷姆 :有时看起来不太明显,因为解释器捆绑在操作系统中


根据这个定义,QBasic是一个编译器。显然,这很麻烦。C#还是一种解释型语言,在某些地方存在一些JITting作为优化。当然,您也可以使用NGen .Net IL,但是您将失去JITter可以进行的优化。从IL中获得的是一个用于将调用分派到子例程中的数据的列表,没有机器代码生成或执行,除非运行时使用率很高,并用JITted机器代码替换p代码数据的运行以替换调用分派。
Bob77

0

如果我们同意解释器的定义«在计算机科学中,解释器是一种计算机程序,可以直接执行(即执行)以编程或脚本语言编写的指令,而无需事先将它们编译成机器语言程序。»毫无疑问:C#不是一种解释语言。

维基百科翻译


0

C#是可编译语言。

可能是因为我也遇到了这样的观点,有人认为存在C#语言的解释器,这是由于类似

C#解释器控制台

或者,例如,著名的

LinqPAD

在这里,您可以只编写代码行并执行它们,这使人认为它是类似于Python的语言,这不是true。它像普通的可编译程序语言一样(从工作流的角度)编译并执行这些行。


1
谢谢。但是从这个角度来看,C ++和C#都是编译语言。但是我认为还不像直接使用C ++将其直接编译为本机代码,而使用.NET仅使用CIL。
约翰五世

1
正如这里回答的那样,C#首先被编译为中间语言,并且仅在以机器特定的机器代码编译之后才被编译。有一个原因检查Eric Lippert的答案 stackoverflow.com/questions/7875253/…以了解其背后的原因。
Tigran 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.