jjzjj

c# - 似乎表达式的某些部分可能在编译时被评估,而其他部分在运行时

coder 2024-05-21 原文

可能是一个愚蠢的问题,因为我可能已经回答了我的问题,但我只是想确保我没有遗漏一些东西

常量表达式在编译时在检查的上下文中计算。我认为不应在编译时计算以下表达式,因为我假设 C# 仅当左侧的所有操作数都是常量时才将特定表达式视为常量表达式:

int i= 100;
long u = (int.MaxValue  + 100 + i); //error

相反,编译器似乎将两个操作数都是常量的任何子表达式视为常量表达式,即使表达式中的其他操作数是非常量?因此编译器可能只在编译时计算表达式的一部分,而表达式的剩余部分(包含非常量值)将在运行时计算 --> 我假设在下面的例子中只有 (200 +100)在编译时被评估
int i=100;
long l = int.MaxValue  + i + ( 200 + 100 ); // works

我的假设正确吗?

谢谢

最佳答案

doec C# classify any subexpression where both operands are constants as a constant expression, even if other operands in an expression are non-constants?



您的问题的答案是"is",但重要的是要清楚地了解什么是“子表达式”。

I assume in "int.MaxValue + i + ( 200 + 100 )" only (200 + 100) gets evaluated at compile time



正确的。现在,如果您改为说“int.MaxValue + i + 200 + 100”,那么“200 + 100”根本不会被计算,因为由于结合性,这不是子表达式。

这有点微妙。让我详细解释一下。

首先,让我们区分法律上的编译时间常量和事实上的编译时间常量。让我给你举个例子。考虑这个方法体:
const int x = 123;
int y = x + x;
if (y * 0 != 0) Console.WriteLine("WOO HOO");
M(ref y);

在 C# 1 和 2 中,如果您使用优化进行编译,则编译时就像您编写的一样:
int y = 246;
M(ref y);

常量 x 消失, y 被初始化为常量折叠表达式,并且算术优化器意识到任何乘以零的局部整数永远不会不等于零,因此它也将其优化掉。

在 C# 3 中,我不小心在优化器中引入了一个错误,该错误在 C# 4 中也未修复。在 C# 3/4 中,我们将其生成为
int y = 246;
bool b = false;
if (b) Console.WriteLine("WOO HOO");
M(ref y);

也就是说,算法被优化掉了,但我们不会更进一步优化掉“if(false)”。

不同之处在于 x + x 上的常量折叠行为保证在编译时发生,但部分变量表达式 y*0 上的常量折叠行为则不然。

我对错误感到抱歉并道歉。但是,我在 C# 3 中更改此设置并在代码生成器中意外引入错误的原因是修复语义分析器中的错误。在 C# 2.0 中这是合法的:
int z;
int y = 246;
if (y * 0 == 0) z = 123;
Console.WriteLine(z);

那不应该是合法的。你知道我也知道 y * 0 == 0 总是为真,因此 z 在读取之前被赋值,但规范说只有在“if”中的表达式是一个编译时才必须执行这种分析-时间常数,这不是编译时常数,因为它包含一个变量。我们对 C# 3.0 进行了重大更改。

所以,好吧,让我们假设您理解了必须评估的法理常量和因为优化器很聪明而评估的事实上的常量之间的区别。您的问题是在什么情况下表达式可以在法律上和事实上在编译时部分“折叠”? (“折叠”是指将包含常量的表达式解析为更简单的表达式。)

我们首先要考虑的是运算符的结合性。 只有在我们完成了关联性和优先级分析之后,我们才知道什么是子表达式,什么不是子表达式。 考虑
int y = 3;
int x = 2 - 1 + y;

加法和减法是左结合的,就像 C# 中的大多数运算符一样,因此与
int x = (2 - 1) + y;

现在显然 (2 - 1) 是一个法理常数表达式,因此折叠并变为 1。

如果另一方面你说
int x = y + 2 - 1;

那是
int x = (y + 2) - 1;

它没有折叠,因为这是两个非常量表达式。

这可能会在检查的上下文中产生实际影响。如果 y 是 int.MaxValue - 1 那么第一个版本不会溢出,但第二个版本会!

现在,编译器和优化器可以说“好吧,我碰巧知道这是一个未经检查的上下文,而且我碰巧知道我可以安全地将它转换为“y + 1”,所以我会的。 C# 编译器不会这样做,但抖动可能会这样做。在您的特定示例中,
long l = int.MaxValue + i + 200 + 100 ; 

实际上是由 C# 编译器生成的代码
long l = ((int.MaxValue + i) + 200) + 100 ; 

并不是
long l = (int.MaxValue + i) + 300; 

抖动可以根据自己的意愿选择进行优化,并且可以证明这样做是安全的。


long l = int.MaxValue + i + (200 + 100); 

当然会被生成为
long l = (int.MaxValue + i) + 300; 

但是,我们确实在 C# 编译器中对字符串执行了您想要的优化!如果你说
string y = whatever;
string x = y + "A" + "B" + "C";

那么你可能会想,好吧,这是一个左关联表达式,所以它是:
string x = ((y + "A") + "B") + "C";

因此不会有恒定的折叠。然而,我们实际上检测到这种情况并在编译时向右折叠,所以我们生成它,就像你写的一样
string x = y + "ABC";

从而节省了运行时串联的成本。字符串连接优化器实际上相当复杂,可以识别在编译时将字符串粘合在一起的各种模式。

我们可以对未经检查的算术做同样的事情。我们只是没有解决它。

关于c# - 似乎表达式的某些部分可能在编译时被评估,而其他部分在运行时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5668536/

有关c# - 似乎表达式的某些部分可能在编译时被评估,而其他部分在运行时的更多相关文章

  1. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  2. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  3. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  4. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  5. ruby-on-rails - 在 Rails 和 ActiveRecord 中查询时忽略某些字段 - 2

    我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr

  6. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  7. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  8. ruby - 调用其他方法的 TDD 方法的正确方法 - 2

    我需要一些关于TDD概念的帮助。假设我有以下代码defexecute(command)casecommandwhen"c"create_new_characterwhen"i"display_inventoryendenddefcreate_new_character#dostufftocreatenewcharacterenddefdisplay_inventory#dostufftodisplayinventoryend现在我不确定要为什么编写单元测试。如果我为execute方法编写单元测试,那不是几乎涵盖了我对create_new_character和display_invent

  9. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  10. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

随机推荐