我每天都使用Javascript和C#,有时我必须考虑使用Javascript时提升。但是,C#似乎没有实现提升(我知道),我无法弄清楚为什么。它更像是一种设计选择还是更类似于适用于所有静态类型语言的安全性或语言约束?
为了记录,我不是说我希望它存在于C#中。我只是想明白为什么不这样做。
编辑:当我在LINQ查询之后声明变量时,我注意到了这个问题,但LINQ查询被推迟到变量声明之后。
var results = From c In db.LoanPricingNoFee Where c.LoanTerm == LoanTerm
&& c.LoanAdvance <= UpperLimit Select c
Order By c.LoanInstalment Ascending;
Int LoanTerm = 12;
引发错误,而:
int LoanTerm = 12;
var results = From c In db.LoanPricingNoFee Where c.LoanTerm == LoanTerm
&& c.LoanAdvance <= UpperLimit Select c
Order By c.LoanInstalment Ascending;
没有。
答案 0 :(得分:9)
在我使用的所有编程语言中,Javascript具有最混乱的范围系统,并且提升是其中的一部分。结果是在JavaScript中编写不可预测的代码很容易,你必须小心编写它以使其成为强大而富有表现力的语言。
与几乎所有其他语言一样,C#假定在声明变量之前不会使用变量。因为它有一个编译器,所以如果你试图使用一个未声明的变量,它可以通过简单地拒绝编译来强制执行。在脚本语言中更常见的另一种方法是,如果在未声明的情况下使用变量,则在首次使用时进行实例化。这可能使得遵循代码流程变得有些困难,并且经常被用作对行为方式的语言的批评。大多数使用具有块级范围的语言(其中变量仅存在于声明它们的级别)的人发现它是Javascript的一个特别奇怪的特性。
提升的几个重要原因可能导致问题:
最后,我无法想象为什么它会成为像C#这样的语言的有用补充 - 它可以带来什么样的好处?
答案 1 :(得分:8)
“它更像是一种设计选择,还是更类似于适用于所有静态类型语言的安全性或语言约束?”
这不是静态类型的约束。编译器将所有变量声明移动到作用域的顶部(在Javascript中这是函数的顶部,在C#中是当前块的顶部)并且如果使用不同类型声明名称时出错将是微不足道的。
因此,C#中不存在提升的原因纯粹是一个设计决策。为什么它的设计方式我不能说我不在团队中。但这可能是因为如果在使用之前总是声明变量,那么解析的简易性(对于人类程序员和编译器都是如此)。
答案 2 :(得分:4)
在Loop-invariant code motion的上下文中存在一种存在于C#(和Java)中的Hoisting形式 - 这是JIT编译器优化,它从循环语句中“提升”(提取)表达式影响实际循环。
您可以详细了解here。
引用:
“Hoisting”是一种编译器优化,可以移动循环不变代码 没有循环。 “循环不变代码”是引用的代码 对循环是透明的,可以用它的值替换,这样 它不会改变循环的语义。此优化得到改善 通过执行代码只执行一次而不是执行运行时性能 每次迭代。
所以这个编写的代码
public void Update(int[] arr, int x, int y)
{
for (var i = 0; i < arr.Length; i++)
{
arr[i] = x + y;
}
}
实际上已经过优化,有点像这样:
public void Update(int[] arr, int x, int y)
{
var temp = x + y;
var length = arr.Length;
for (var i = 0; i < length; i++)
{
arr[i] = temp;
}
}
这种情况发生在JIT中 - 即将IL转换为本机机器指令时,不容易查看(您可以查看here和here)。
我不是阅读汇编的专家,但这是我使用BenchmarkDotNet运行此片段所得到的,我对它的评论显示优化实际发生了:
int[] arr = new int[10];
int x = 11;
int y = 19;
public void Update()
{
for (var i = 0; i < arr.Length; i++)
{
arr[i] = x + y;
}
}
生成:
答案 3 :(得分:3)
因为它是一个错误的概念,很可能因为急于实现JavaScript而存在。这是一种糟糕的编码方法,即使是经验丰富的javascript编码器也会误导变量的范围。
答案 4 :(得分:2)
函数提升在编译器必须完成的工作中可能存在不必要的成本。例如,如果由于各种代码控制决策返回函数而从未达到变量声明,则处理器不需要浪费时间将未定义的空引用变量推送到堆栈内存,然后将其从堆栈中弹出作为这是方法的清理操作,甚至没有达到。
另外,请记住JavaScript具有“变量提升”和“功能提升”(以及其他),这些处理方式不同。函数提升在C#中没有意义,因为它不是自上而下的解释语言。编译代码后,可能永远不会调用该方法。但是,在JavaScript中,当解释器解析它们时,会立即评估“自调用”函数。
我怀疑这是一个任意的设计决定:不仅提升C#效率低下,而且对C#的工作方式也没有意义。