我在下面展示的是一个理论问题。但我对新的 C#7 编译器如何工作和解析本地函数很感兴趣。
在 C#7 中,我可以使用本地函数。例如(您可以在 LinqPad beta 中尝试这些示例):
示例 1: 嵌套 Main()
void Main()
{
void Main()
{
Console.WriteLine("Hello!");
}
Main();
}
不是以递归方式调用 Main(),而是调用局部函数 Main() 一次,因此输出为:
Hello!
编译器在没有警告和错误的情况下接受它。
示例 2: 在这里,我要更深入一点,例如:
在这种情况下,我也希望得到相同的输出,因为调用了最内层的局部函数,然后向上一层 Main() 只是另一个具有局部作用域的局部函数,所以它应该与第一个示例没有太大区别。
但令我惊讶的是,我得到了一个错误:
CS0136 A local or parameter named 'Main' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter
问题: 您能解释一下为什么在示例 2 中会出现此错误,而在示例 1 中不会吗?
我想,每个内部 Main() 都会有一个本地作用域并隐藏在外面。
更新: 感谢迄今为止所有做出贡献的人(无论是回答还是评论),您所写的内容对于理解 C# 编译器的行为非常有值(value)。
根据我阅读的内容以及在考虑各种可能性之后,我在您的帮助下发现它可能是编译器错误或设计行为。
剧透:
我们得出的结论是这是一个设计选择,而不是错误。
回想一下,C# 有一些与 C++ 等语言不同的设计目标。
如果您对我为进一步调查所做的工作感兴趣:
我已将最里面的函数重命名为 MainL,例如:
示例 2b:
void Main()
{
void Main()
{
void MainL()
{
Console.WriteLine("Hello!");
}
MainL();
}
Main();
}
此修改后的示例编译并运行成功。
现在当你用 LinqPad 编译它时然后切换到 IL 选项卡,您可以看到编译器做了什么:
它创建了最内层的 MainL 函数作为 g__MainL0_1,封闭的 Main 函数具有标签 g__Main0_0。
这意味着,如果您从 MainL 中删除 L,您会注意到编译器已经以独特的方式重命名了它,因为这样代码看起来像:
IL_0000: call UserQuery.<Main>g__Main0_0
IL_0005: ret
<Main>g__Main0_0:
IL_0000: call UserQuery.<Main>g__Main0_1
IL_0005: ret
<Main>g__Main0_1:
IL_0000: ldstr "Hello!"
IL_0005: call System.Console.WriteLine
IL_000A: ret
这仍然会正确解析。由于代码在示例 2 中看起来不像这样,因为编译器因错误而停止,我现在假设该行为是设计使然,它不太可能是编译器错误。
结论: 你们中的一些人写道,在 C++ 中,局部函数的递归解析会导致重构问题,而另一些人则写道,C# 中的这种行为是编译器对局部变量所做的(注意错误消息是相同的)——甚至证实我认为这是设计使然,没有错误。
最佳答案
封闭范围内的参数和局部变量在局部函数内可用。
I thought, each inner Main() would have a local scope and is hidden outside.
C# 不会覆盖父作用域中的名称,因此在当前作用域和父作用域中定义的局部名称 Main 存在歧义。
因此在第二个示例中,void Main() 的两个声明都可用于内部作用域,编译器会向您显示错误。
这是一个包含变量和局部函数 的示例,可以帮助您在熟悉的环境中查看问题。为了清楚地表明这只是范围问题,我修改了示例并向变量添加了函数以使其清楚:
class Test
{
int MainVar = 0;
public void Main()
{
if (this.MainVar++ > 10) return;
int MainVar = 10;
Console.WriteLine($"Instance Main, this.MainVar=${this.MainVar}, MainVar={MainVar}");
void Main()
{
if (MainVar++ > 14) return;
Console.WriteLine($"Local Main, this.MainVar=${this.MainVar}, MainVar={MainVar}");
// Here is a recursion you were looking for, in Example 1
this.Main();
// Let's try some errors!
int MainVar = 110; /* Error! Local MainVar is already declared in a parent scope.
// Error CS0136 A local or parameter named 'MainVar' cannot be declared in this scope
// because that name is used in an enclosing local scope to define a local or parameter */
void Main() { } /* Error! The same problem with Main available on the parent scope.
// Error CS0136 A local or parameter named 'Main' cannot be declared in this scope
// because that name is used in an enclosing local scope to define a local or parameter */
}
Main(); // Local Main()
this.Main(); // Instance Main()
// You can have another instance method with a different parameters
this.Main(99);
// But you can't have a local function with the same name and parameters do not matter
void Main(int y) { } // Error! Error CS0128 A local variable or function named 'Main' is already defined in this scope
}
void Main(int x)
{
Console.WriteLine($"Another Main but with a different parameter x={x}");
}
}
当您尝试覆盖局部变量和局部函数时,甚至会出现相同的错误。
如您所见,这是一个作用域问题,您不能覆盖局部函数或变量。
顺便说一句,在第一个示例中,您可以使用 this.Main(); 进行递归调用:
void Main()
{
void Main()
{
Console.WriteLine("Hello!");
}
this.Main(); // call instance method
}
脚注:局部函数并不像某些评论员建议的那样表示为委托(delegate),这使得局部函数在内存和 CPU 方面都更加精简。
关于c# - 为什么局部函数并不总是隐藏在 C#7 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41288668/
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?