“带有...结束于”真的更有效吗? [英] Is a 'With ... End With' really more efficient?

查看:68
本文介绍了“带有...结束于”真的更有效吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

So I'm playing with ILDASM and noticed an oddity that I can't find a really good explanation for on Google.

似乎在使用With块时在VB.NET中,生成的MSIL大于w / o。因此,这引起了我的疑问,With Blocks真的更有效吗? MSIL是将JIT绑定到本机代码中的原因,因此较小的代码大小应意味着更有效的代码,对吗?

It seems that when using With blocks in VB.NET, the resulting MSIL larger than w/o. So this leads me to ask, are With Blocks really more efficient? MSIL is what gets JITed into native machine code, so smaller code size should imply more efficient code, right?

这里是两个类(Class2和Class3)的示例,为Class1的实例设置相同的值。 Class2不用With块来做,而Class3使用With。 Class1具有六个属性,涉及6个私有成员。每个成员都是特定的数据类型,并且都是该测试用例的一部分。

Here's a sample of two classes (Class2 and Class3), which set the same values for an instance of Class1. Class2 does it without a With block, while Class3 uses With. Class1 has six properties, touching 6 private members. Each member is of a specific data type, and it's all a part of this testcase.

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:

Here's the resulting MSIL for Class2:

.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:

And here is the MSIL for Class3:

.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。

The only major difference I can discern at a glance is the use of the ldloc.1 opcode over ldloc.0. Per MSDN, the difference between these two is negligible, with ldloc.0 being an efficient method of using ldloc to access a local variable at index 0, and ldloc.1 being the same, just for index 1.

Class3的代码大小是88对84。这来自Release / Optimized版本。内置于VB Express 2010中的.NET 4.0 Framework客户端配置文件。

Note that Class3's code size is 88 versus 84. These are from the Release/Optimized builds. Built in VB Express 2010, .NET 4.0 Framework Client Profile.

有何想法?

编辑:

我想为那些绊在这个线程上的人添加答案的一般要点


Wanted to add for those stumbling on this thread the generic gist of the answers, as I understand them.

合理使用有...结束于

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

不明智使用 With ...结尾为

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

原因是因为在ObjectA的示例中,您要减少几个成员,并且该成员的每个分辨率都需要做一些工作,所以通过仅解析引用一次并将最终引用粘贴到temp变量中(仅此而已) 确实具有),这可以加快访问隐藏在该对象深处的属性/方法的速度。

The reason is because with ObjectA's example, you're going several members down, and each resolution of that member takes some work, so by only resolving the references one time and sticking the final reference into a temp variable (which is all that With really does), this speeds up accessing the properties/methods hidden deep in that object.

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

ObjectB is not as efficient because you're only going one level deep. Each resolution is about the same as accessing the temp reference created by the With statement, so there is little-to-no gain in performance.

推荐答案

这是使用With语句的类中的说明性部分:

This is the instructive section, from the class that uses the With statement:

IL_000b:  stloc.0
IL_000c:  ldloc.0
IL_000d:  stloc.1
IL_000e:  ldloc.1

零索引指令出现在类中,该类也也使用With语句,它们对应于的实例化。源代码中的c1( Dim c1作为New Class1

The zero-indexed instructions appear in the class which does not use the With statement as well, and they correspond to the instantiation of c1 in the source (Dim c1 As New Class1)

>使用使用With语句表示在堆栈上创建了一个新的局部变量。这就是With语句的作用:在后台,它实例化With语句中引用的对象的本地副本。可以提高性能的原因是,如果访问实例是一项昂贵的操作,则与缓存属性的本地副本可以提高性能的原因相同。每次更改其属性之一时,都不必再次检索该对象本身。

The one-indexed instructions in the class that does use the With statement indicates that a new local variable is created on the stack. That's what the With statement does: behind the scenes, it instantiates a local copy of the object referenced in the With statement. The reason this can improve performance is if accessing the instance is a costly operation, the same reason as caching a local copy of a property can improve performance. The object itself doesn't have to be retrieved again each time one of its properties is changed.

您还可以看到,您看到了 ldloc.1。 而不是IL中使用With语句的类中的 ldloc.0 。这是因为使用了对With语句创建的局部变量(评估堆栈中的第二个变量)的引用,而不是评估堆栈中的第一个变量(将Class1实例化为变量 c1 )。

You also observe that you see ldloc.1 instead of ldloc.0 in the IL for the class that uses the With statement. This is because the reference to the local variable created by the With statement (the second variable in the evaluation stack) is being used, as opposed to the first variable in the evaluation stack (the instantiation of Class1 as the variable c1).

这篇关于“带有...结束于”真的更有效吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆