在foreach定义中调用函数是浪费时间吗?

时间:2016-07-26 13:17:44

标签: vb.net loops iteration

有时我必须运行一个循环,其中列表或数组来自函数。

我通常这样做:

Dim list = SomeClass.GetMyList()
For Each item in list
  'Do some stuff
Next

是否与:

相同
For Each item in SomeClass.GetMyList()
  'Do some stuff
Next

我通常采用第一种方式,因为我认为第二种方式是每次开始下一次迭代时都会进行调用,因此会浪费一些时间。

我是这样认为的吗?或者我可以继续第二种方式,因为编译器足够聪明,不会每轮都进行一次调用吗?

3 个答案:

答案 0 :(得分:3)

只有for块内的 重复,而不是其初始化。

您的第二个选项与第一个选项相同,只是使用一个未命名的临时变量,其结果为GetMyList()。如果有的话,由于这个原因,它可能更多有效......尽管一个好的优化器会使两段代码无论如何相同。

正如评论中所提到的,调试人员会非常清楚这一点,并且无数其他原因是一个非常宝贵的工具。

答案 1 :(得分:2)

方法#1 会在方法的其余部分范围内为您提供对列表的引用。

方法#2 在引用列表的幕后创建一个变量,但在for循环后该变量超出范围

对于范围界定,我更喜欢#2,但我也对简洁的代码保持公正。如果GetMyList返回引用类型(例如List<T>或数组),则可能会出现一些意外的副作用。

Public Sub Foo()
    Dim someClass As New SomeClass()
    ' this variable stays in scope after the following For Each loop
    Dim list = someClass.GetMyList()
    For Each item In list
        Console.Write(item)
    Next
    Console.WriteLine()
    ' now we can sort the backing field - did you intend for this to happen?
    list.Sort()
    ' the following For Each loop doesn't leave any reference behind
    For Each item In someClass.GetMyList()
        Console.Write(item)
    Next
End Sub

Private Class SomeClass
    Private _list As List(Of Integer) = {3, 2, 1}.ToList()
    Public Function GetMyList() As List(Of Integer)
        Return _list
    End Function
End Class

Foo()写道:

  

321

     

123

所以你可以在完成它之后实际操作后备区域!

答案 2 :(得分:1)

让我们举一个简单的例子

信息不多,我认为GetMyListlist(of integer)

Module Module1
    Sub Main()

        test1()
        test2()

        Console.ReadKey(False)
    End Sub

    Sub test1()
        Dim list = SomeClass.GetMyList()
        For Each item In list
            Console.WriteLine(item)
        Next
    End Sub

    Sub test2()
        For Each item In SomeClass.GetMyList()
            Console.WriteLine(item)
        Next
    End Sub
End Module

Class SomeClass
    Public Shared Function GetMyList() As List(Of Integer)
        Dim aList = New List(Of Integer)

        aList.Add(1)
        aList.Add(2)
        aList.Add(3)

        Console.WriteLine("I am in the function now")

        Return aList
    End Function
End Class

您可以自己运行以查看行为

现在让我们看一下实际的IL(用debug编译)

test1代码;

.method public static 
    void test1 () cil managed 
{
    // Method begins at RVA 0x2120
    // Code size 64 (0x40)
    .maxstack 1
    .locals init (
        [0] class [mscorlib]System.Collections.Generic.List`1<int32> list,
        [1] int32 item,
        [2] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> VB$t_struct$L0,
        [3] bool VB$CG$t_bool$S0
    )

    IL_0000: nop
    IL_0001: call class [mscorlib]System.Collections.Generic.List`1<int32> ConsoleApplication1.SomeClass::GetMyList()
    IL_0006: stloc.0
    IL_0007: nop
    .try
    {
        IL_0008: ldloc.0
        IL_0009: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<int32>::GetEnumerator()
        IL_000e: stloc.2
        IL_000f: br.s IL_0021
        // loop start (head: IL_0021)
            IL_0011: ldloca.s VB$t_struct$L0
            IL_0013: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
            IL_0018: stloc.1
            IL_0019: ldloc.1
            IL_001a: call void [mscorlib]System.Console::WriteLine(int32)
            IL_001f: nop
            IL_0020: nop

            IL_0021: ldloca.s VB$t_struct$L0
            IL_0023: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()
            IL_0028: stloc.3
            IL_0029: ldloc.3
            IL_002a: brtrue.s IL_0011
        // end loop

        IL_002c: nop
        IL_002d: leave.s IL_003e
    } // end .try
    finally
    {
        IL_002f: ldloca.s VB$t_struct$L0
        IL_0031: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>
        IL_0037: callvirt instance void [mscorlib]System.IDisposable::Dispose()
        IL_003c: nop
        IL_003d: endfinally
    } // end handler

    IL_003e: nop
    IL_003f: ret
} // end of method Module1::test1

test2代码;

.method public static 
    void test2 () cil managed 
{
    // Method begins at RVA 0x217c
    // Code size 62 (0x3e)
    .maxstack 1
    .locals init (
        [0] int32 item,
        [1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> VB$t_struct$L0,
        [2] bool VB$CG$t_bool$S0
    )

    IL_0000: nop
    IL_0001: nop
    .try
    {
        IL_0002: call class [mscorlib]System.Collections.Generic.List`1<int32> ConsoleApplication1.SomeClass::GetMyList()
        IL_0007: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<int32>::GetEnumerator()
        IL_000c: stloc.1
        IL_000d: br.s IL_001f
        // loop start (head: IL_001f)
            IL_000f: ldloca.s VB$t_struct$L0
            IL_0011: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
            IL_0016: stloc.0
            IL_0017: ldloc.0
            IL_0018: call void [mscorlib]System.Console::WriteLine(int32)
            IL_001d: nop
            IL_001e: nop

            IL_001f: ldloca.s VB$t_struct$L0
            IL_0021: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()
            IL_0026: stloc.2
            IL_0027: ldloc.2
            IL_0028: brtrue.s IL_000f
        // end loop

        IL_002a: nop
        IL_002b: leave.s IL_003c
    } // end .try
    finally
    {
        IL_002d: ldloca.s VB$t_struct$L0
        IL_002f: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>
        IL_0035: callvirt instance void [mscorlib]System.IDisposable::Dispose()
        IL_003a: nop
        IL_003b: endfinally
    } // end handler

    IL_003c: nop
    IL_003d: ret
} // end of method Module1::test2

唯一不同的是在内存中调用/加载GetMyList引用时

首先在局部变量中加载它,第二个在循环开始时加载它

因此两种情况都会做同样的事情。