jjzjj

c# - 带有 Nullable<T> 的 '==' 的参数顺序

coder 2023-07-13 原文

以下两个 C# 函数的不同之处仅在于将参数的左/右顺序交换为 equals 运算符 ==。 (IsInitialized 的类型是bool)。使用 C# 7.1.NET 4.7

static void A(ISupportInitialize x)
{
    if ((x as ISupportInitializeNotification)?.IsInitialized == true)
        throw null;
}
static void B(ISupportInitialize x)
{
    if (true == (x as ISupportInitializeNotification)?.IsInitialized)
        throw null;
}

但是第二个的IL 代码 似乎要复杂得多。例如,B 是:

  • 长 36 个字节(IL 代码);
  • 调用其他函数,包括 newobjinitobj
  • 声明四个本地人与一个本地人。

函数“A”的 IL…

[0] bool flag
        nop
        ldarg.0
        isinst [System]ISupportInitializeNotification
        dup
        brtrue.s L_000e
        pop
        ldc.i4.0
        br.s L_0013
L_000e: callvirt instance bool [System]ISupportInitializeNotification::get_IsInitialized()
L_0013: stloc.0
        ldloc.0
        brfalse.s L_0019
        ldnull
        throw
L_0019: ret

函数“B”的 IL…

[0] bool flag,
[1] bool flag2,
[2] valuetype [mscorlib]Nullable`1<bool> nullable,
[3] valuetype [mscorlib]Nullable`1<bool> nullable2
        nop
        ldc.i4.1
        stloc.1
        ldarg.0
        isinst [System]ISupportInitializeNotification
        dup
        brtrue.s L_0018
        pop
        ldloca.s nullable2
        initobj [mscorlib]Nullable`1<bool>
        ldloc.3
        br.s L_0022
L_0018: callvirt instance bool [System]ISupportInitializeNotification::get_IsInitialized()
        newobj instance void [mscorlib]Nullable`1<bool>::.ctor(!0)
L_0022: stloc.2
        ldloc.1
        ldloca.s nullable
        call instance !0 [mscorlib]Nullable`1<bool>::GetValueOrDefault()
        beq.s L_0030
        ldc.i4.0
        br.s L_0037
L_0030: ldloca.s nullable
        call instance bool [mscorlib]Nullable`1<bool>::get_HasValue()
L_0037: stloc.0
        ldloc.0
        brfalse.s L_003d
        ldnull
        throw
L_003d: ret

问题

  1. AB 之间是否存在任何功能、语义或其他实质性运行时差异? (我们只对这里的正确性感兴趣,而不是性能)
  2. 如果它们在功能上相同,那么可以暴露可观察到差异的运行时条件是什么?
  3. 如果它们功能等价物,B在做什么(总是以与A相同的结果结束),以及什么触发了它的痉挛? B是否有永远无法执行的分支?
  4. 如果差异可以用 ==左侧 之间的差异来解释,(这里是属性引用表达式与文字值),可以您指定了描述详细信息的 C# 规范部分。
  5. 是否有可靠的经验法则可用于在编码时预测臃肿的 IL,从而避免创建它?

奖金。每个堆栈的最终 JITted x86AMD64 代码如何叠加?


[编辑]

根据评论中的反馈添加的附加说明。首先,提出了第三个变体,但它提供与 A 相同的 IL(对于 DebugRelease 构建)。然而,从语法上看,新版本的 C# 确实比 A 更时尚:

static void C(ISupportInitialize x)
{
    if ((x as ISupportInitializeNotification)?.IsInitialized ?? false)
        throw null;
}

这里还有每个函数的 Release IL。请注意,A/CB 的不对称性在 Release IL 中仍然很明显,因此原始问题依然屹立不倒。

为功能“A”、“C”…发布 IL

        ldarg.0
        isinst [System]ISupportInitializeNotification
        dup
        brtrue.s L_000d
        pop
        ldc.i4.0
        br.s L_0012
L_000d: callvirt instance bool [System]ISupportInitializeNotification::get_IsInitialized()
        brfalse.s L_0016
        ldnull
        throw
L_0016: ret

为功能“B”发布 IL…

[0] valuetype [mscorlib]Nullable`1<bool> nullable,
[1] valuetype [mscorlib]Nullable`1<bool> nullable2
        ldc.i4.1
        ldarg.0
        isinst [System]ISupportInitializeNotification
        dup
        brtrue.s L_0016
        pop
        ldloca.s nullable2
        initobj [mscorlib]Nullable`1<bool>
        ldloc.1
        br.s L_0020
L_0016: callvirt instance bool [System]ISupportInitializeNotification::get_IsInitialized()
        newobj instance void [mscorlib]Nullable`1<bool>::.ctor(!0)
L_0020: stloc.0
        ldloca.s nullable
        call instance !0 [mscorlib]Nullable`1<bool>::GetValueOrDefault()
        beq.s L_002d
        ldc.i4.0
        br.s L_0034
L_002d: ldloca.s nullable
        call instance bool [mscorlib]Nullable`1<bool>::get_HasValue()
L_0034: brfalse.s L_0038
        ldnull
        throw
L_0038: ret

最后,提到了一个使用新的 C# 7 语法的版本,它似乎产生了所有 IL 中最干净的:

static void D(ISupportInitialize x)
{
    if (x is ISupportInitializeNotification y && y.IsInitialized)
        throw null;
}

为函数“D”释放 IL…

[0] class [System]ISupportInitializeNotification y
        ldarg.0
        isinst [System]ISupportInitializeNotification
        dup
        stloc.0
        brfalse.s L_0014
        ldloc.0
        callvirt instance bool [System]ISupportInitializeNotification::get_IsInitialized()
        brfalse.s L_0014
        ldnull
        throw
L_0014: ret

最佳答案

所以我对答案很好奇并查看了 c# 6 规范(不知道托管 c# 7 规范的位置)。完全免责声明:我不保证我的回答是正确的,因为我没有编写 c# 规范/编译器并且我对内部的理解是有限的。

但我认为答案在于 overloadable 的结果==运算符(operator)。 == 的最佳适用过载通过使用 better function members 的规则确定.

来自规范:

Given an argument list A with a set of argument expressions {E1, E2, ..., En} and two applicable function members Mp and Mq with parameter types {P1, P2, ..., Pn} and {Q1, Q2, ..., Qn}, Mp is defined to be a better function member than Mq if

for each argument, the implicit conversion from Ex to Qx is not better than the implicit conversion from Ex to Px, and for at least one argument, the conversion from Ex to Px is better than the conversion from Ex to Qx.

引起我注意的是参数列表 {E1, E2, .., En} .如果你比较 Nullable<bool>bool参数列表应该类似于 {Nullable<bool> a, bool b}并为该参数列出 Nullable<bool>.Equals(object o)方法似乎是最好的函数,因为它只需要从 bool 进行一次隐式转换。至 object .

但是,如果您将参数列表的顺序恢复为 {bool a, Nullable<bool> b} Nullable<bool>.Equals(object o)方法不再是最好的功能,因为现在你必须从 Nullable<bool> 转换至 bool在第一个参数中,然后来自 boolobject在第二个参数中。这就是为什么对于案例 A 选择了不同的重载,这似乎导致了更清晰的 IL 代码。

同样,这是满足我自己好奇心的解释,似乎符合 c# 规范。但我还没有弄清楚如何调试编译器以查看实际发生的情况。

关于c# - 带有 Nullable<T> 的 '==' 的参数顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45751945/

有关c# - 带有 Nullable<T> 的 '==' 的参数顺序的更多相关文章

  1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  2. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  3. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  4. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  5. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  6. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  7. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  8. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  9. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

    我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

  10. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

随机推荐