vb.net - 'With ... End With' 真的更高效吗?

标签 vb.net cil

所以我在玩 ILDASM 时发现了一个奇怪的现象,我在 Google 上找不到很好的解释。

似乎在 VB.NET 中使用 With block 时,生成的 MSIL 比 w/o 更大。所以这让我想问,With Blocks 真的更高效吗? MSIL 是 JIT 到 native 机器代码中的工具,因此较小的代码大小应该意味着更高效的代码,对吗?

下面是两个类(Class2 和 Class3)的示例,它们为 Class1 的实例设置相同的值。 Class2 不使用 With block 来完成此操作,而 Class3 使用 With block 。 Class1 有 6 个属性,涉及 6 个私有(private)成员。每个成员都具有特定的数据类型,并且都是此测试用例的一部分。

Friend Class Class2
    Friend Sub New()
        Dim c1 As New Class1

        c1.One = "foobar"
        c1.Two = 23009
        c1.Three = 3987231665
        c1.Four = 2874090071765301873
        c1.Five = 3.1415973801462975
        c1.Six = "a"c
    End Sub
End Class

Friend Class Class3
    Friend Sub New()
        Dim c1 As New Class1

        With c1
            .One = "foobar"
            .Two = 23009
            .Three = 3987231665
            .Four = 2874090071765301873
            .Five = 3.1415973801462975
            .Six = "a"c
        End With
    End Sub
End Class

这是 Class2 的最终 MSIL:

.method assembly specialname rtspecialname 
        instance void  .ctor() cil managed
{
    // Code size       84 (0x54)
    .maxstack  2
    .locals init ([0] class WindowsApplication1.Class1 c1)
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  newobj     instance void WindowsApplication1.Class1::.ctor()
    IL_000b:  stloc.0
    IL_000c:  ldloc.0
    IL_000d:  ldstr      "foobar"
    IL_0012:  callvirt   instance void WindowsApplication1.Class1::set_One(string)
    IL_0017:  ldloc.0
    IL_0018:  ldc.i4     0x59e1
    IL_001d:  callvirt   instance void WindowsApplication1.Class1::set_Two(int16)
    IL_0022:  ldloc.0
    IL_0023:  ldc.i4     0xeda853b1
    IL_0028:  callvirt   instance void WindowsApplication1.Class1::set_Three(uint32)
    IL_002d:  ldloc.0
    IL_002e:  ldc.i8     0x27e2d1b1540c3a71
    IL_0037:  callvirt   instance void WindowsApplication1.Class1::set_Four(uint64)
    IL_003c:  ldloc.0
    IL_003d:  ldc.r8     3.1415973801462975
    IL_0046:  callvirt   instance void WindowsApplication1.Class1::set_Five(float64)
    IL_004b:  ldloc.0
    IL_004c:  ldc.i4.s   97
    IL_004e:  callvirt   instance void WindowsApplication1.Class1::set_Six(char)
    IL_0053:  ret
} // end of method Class2::.ctor

这是 Class3 的 MSIL:

.method assembly specialname rtspecialname 
        instance void  .ctor() cil managed
{
    // Code size       88 (0x58)
    .maxstack  2
    .locals init ([0] class WindowsApplication1.Class1 c1,
                  [1] class WindowsApplication1.Class1 VB$t_ref$L0)
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  newobj     instance void WindowsApplication1.Class1::.ctor()
    IL_000b:  stloc.0
    IL_000c:  ldloc.0
    IL_000d:  stloc.1
    IL_000e:  ldloc.1
    IL_000f:  ldstr      "foobar"
    IL_0014:  callvirt   instance void WindowsApplication1.Class1::set_One(string)
    IL_0019:  ldloc.1
    IL_001a:  ldc.i4     0x59e1
    IL_001f:  callvirt   instance void WindowsApplication1.Class1::set_Two(int16)
    IL_0024:  ldloc.1
    IL_0025:  ldc.i4     0xeda853b1
    IL_002a:  callvirt   instance void WindowsApplication1.Class1::set_Three(uint32)
    IL_002f:  ldloc.1
    IL_0030:  ldc.i8     0x27e2d1b1540c3a71
    IL_0039:  callvirt   instance void WindowsApplication1.Class1::set_Four(uint64)
    IL_003e:  ldloc.1
    IL_003f:  ldc.r8     3.1415973801462975
    IL_0048:  callvirt   instance void WindowsApplication1.Class1::set_Five(float64)
    IL_004d:  ldloc.1
    IL_004e:  ldc.i4.s   97
    IL_0050:  callvirt   instance void WindowsApplication1.Class1::set_Six(char)
    IL_0055:  ldnull
    IL_0056:  stloc.1
    IL_0057:  ret
} // end of method Class3::.ctor

我一眼就能看出的唯一主要区别是 ldloc.1 的使用操作码 ldloc.0 。根据 MSDN,这两者之间的差异可以忽略不计,ldloc.0是一种有效的使用方法ldloc访问索引 0 处的局部变量,以及 ldloc.1相同,只是索引 1。

请注意,Class3 的代码大小是 88 与 84。这些来自发布/优化版本。内置 VB Express 2010、.NET 4.0 Framework 客户端配置文件。

想法?

编辑:
我想为那些在这个帖子上遇到困难的人添加我所理解的答案的一般要点。

合理使用With ... End With :

With ObjectA.Property1.SubProperty7.SubSubProperty4
    .SubSubSubProperty1 = "Foo"
    .SubSubSubProperty2 = "Bar"
    .SubSubSubProperty3 = "Baz"
    .SubSubSubProperty4 = "Qux"
End With

不合理地使用 With ... End With :

With ObjectB
    .Property1 = "Foo"
    .Property2 = "Bar"
    .Property3 = "Baz"
    .Property4 = "Qux"
End With

原因是因为在 ObjectA 的示例中,您将删除多个成员,并且该成员的每个解析都需要一些工作,因此只需解析一次引用并将最终引用粘贴到临时变量中(这就是全部) With 确实如此),这可以加快访问隐藏在该对象深处的属性/方法的速度。

ObjectB 效率不高,因为您只深入一层。每个分辨率与访问 With 创建的临时引用大致相同。声明,因此性能几乎没有提升。

最佳答案

查看 IL 代码,With block 的作用基本上是:

Friend Class Class3
  Friend Sub New()
    Dim c1 As New Class1
    Dim temp as Class1 = c1
    temp.One = "foobar"
    temp.Two = 23009
    temp.Three = 3987231665
    temp.Four = 2874090071765301873
    temp.Five = 3.1415973801462975
    temp.Six = "a"c
    temp = Nothing
  End Sub
End Class

但重要的是 JIT 编译器如何处理它。语言编译器没有做太多优化,这主要留给 JIT 编译器。最有可能的是,它会发现变量 c1 除了创建另一个变量之外没有用于任何其他用途,并完全优化 c1 的存储。

无论哪种方式,如果它仍然创建另一个变量,那都是一个非常便宜的操作。即使有任何性能差异,也非常小,并且可能会下降。

关于vb.net - 'With ... End With' 真的更高效吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4094378/

相关文章:

.net - 如何在 VB.NET 中的用户机器上安装字体以便 Word 等应用程序可以使用它?

asp.net:迭代多个repeater.items 集合

mysql - 如何在VB.NET中等待MySQL更新?

asp.net - AJAX 更新面板给出错误

.net - 如何检测哪种 .NET 语言正在调用我的代码

c# - 方法名称会被编译到 EXE 中吗?

asp.net - Visual basic以编程方式将用户名和密码传递给https url,使网络浏览器显示网页并从网页下载

c++-cli - VS2015 C++/CLI Release Build 中的 NullReferenceException

c# - Msil 以动态类型发出静态数组

c# - 有没有办法查看 DynamicMethod 生成的 x86 汇编代码?