6

If我和一位同事对陈述及其表现有不同的看法。我的观点是If...ElseIf应该使用语句。他的观点是他不相信,并且用嵌套语句ElseIf编写所有内容。If

假设在这种情况下不能使用 case 语句。我想知道的是,使用嵌套If..Else语句与 usingIf...ElseIf语句执行代码的效率如何。我知道代码可读性是一个因素,但这不应该影响性能。

让我们看看下面的例子。

使用 If Else:

If () then
    'Do something'
Else
    If () then
        'Do something'
    Else
        If () then
            'Do something'
        Else
            If () then
                'Do something'
            Else
                'Do something else'
            End If
        End If
    End If
End If

使用 ElseIf:

If () then
    'Do something'
ElseIf () then
    'Do something'
ElseIf () then
    'Do something'
ElseIf () then
    'Do something'
Else
    'Do something else'
End If

我知道这是一个小规模的例子,但可以说像这样的块在整个应用程序中被大量使用。

两个代码部分之间是否存在任何性能差异,或者在编译应用程序后它们的性能几乎相同?

####更新#####

我创建了一个程序来测试通过函数运行 x 次。

Public Class Form1

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    For i As Integer = 0 To 1000
        Run()
    Next
End Sub

Sub Run()

    Dim Time1Start As Integer = 0
    Dim Time1End As Integer = 0
    Dim Time2Start As Integer = 0
    Dim Time2End As Integer = 0

    Time2Start = CInt(DateTime.Now.ToString("fff"))
    runElse()
    Time2End = CInt(DateTime.Now.ToString("fff"))

    Time1Start = CInt(DateTime.Now.ToString("fff"))
    runElseIf()
    Time1End = CInt(DateTime.Now.ToString("fff"))

    TextBox1.Text += If(Time1End < Time1Start, Time1End + (1000 - Time1Start), Time1End - Time1Start) & vbTab & If(Time2End < Time2Start, Time2End + (1000 - Time2Start), Time2End - Time2Start) & vbCrLf
End Sub

Sub runElseIf()
    If sleep(10) Then
        'Do something'
    Else
        If sleep(10) Then
            'Do something'
        Else
            If sleep(10) Then
                'Do something'
            Else
                If sleep(10) Then
                    'Do something'
                Else
                    If sleep(10) Then
                        'Do something'
                    Else
                        If sleep(10) Then
                            'Do something'
                        Else
                            If sleep(10) Then
                                'Do something'
                            Else
                                If sleep(10) Then
                                    'Do something'
                                Else
                                    If sleep(10) Then
                                        'Do something'
                                    Else
                                        If sleep(10) Then
                                            'Do something'
                                        Else
                                            'Do something else'
                                        End If
                                    End If
                                End If
                            End If
                        End If
                    End If
                End If
            End If
        End If
    End If
End Sub

Sub runElse()
    If sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    ElseIf sleep(10) Then
        'Do something'
    Else
        'Do something else'
    End If
End Sub

Function sleep(ByVal ms As Integer) As Integer
    System.Threading.Thread.Sleep(ms)
    Return False
End Function

End Class

我运行了程序,这是我的结果:
500 次循环平均 - ElseIf:108.248ms If Else:106.507ms
1000 循环平均 - ElseIf:107.747ms If Else:107.451ms(Else 如果先运行)
1000 循环平均 - ElseIf:107.683ms If Else: 107.076ms (ElseIf 先运行)

也许使用更大的数据集,数字会发生变化,但在这 3 次试验中,实际上似乎If Else优于ElseIf陈述。

4

7 回答 7

8

我已经对两者进行了反编译,它似乎生成了相同的代码(使用 ildasm)。这是一个非常简单的 If 语句,不同的 If 可能会得到不同的结果。我建议你对你的代码做同样的事情,然后看看。

Module Module1

    Sub Main()

        Dim a As Integer
        Dim i As Integer = 1

        If i = 1 Then
            a = 9
        ElseIf i = 2 Then
            a = 8
        ElseIf i = 3 Then
            a = 7
        Else
            a = 6
        End If

    End Sub

End Module



.method public static void  Main() cil managed
{
  .entrypoint
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       30 (0x1e)
  .maxstack  2
  .locals init ([0] int32 a,
           [1] int32 i)
  IL_0000:  ldc.i4.1
  IL_0001:  stloc.1
  IL_0002:  ldloc.1
  IL_0003:  ldc.i4.1
  IL_0004:  bne.un.s   IL_000b
  IL_0006:  ldc.i4.s   9
  IL_0008:  stloc.0
  IL_0009:  br.s       IL_001d
  IL_000b:  ldloc.1
  IL_000c:  ldc.i4.2
  IL_000d:  bne.un.s   IL_0013
  IL_000f:  ldc.i4.8
  IL_0010:  stloc.0
  IL_0011:  br.s       IL_001d
  IL_0013:  ldloc.1
  IL_0014:  ldc.i4.3
  IL_0015:  bne.un.s   IL_001b
  IL_0017:  ldc.i4.7
  IL_0018:  stloc.0
  IL_0019:  br.s       IL_001d
  IL_001b:  ldc.i4.6
  IL_001c:  stloc.0
  IL_001d:  ret
} // end of method Module1::Main

而另一个

Module Module1

    Sub Main()

        Dim a As Integer
        Dim i As Integer = 1

        If i = 1 Then
            a = 9
        Else
            If i = 2 Then
                a = 8
            Else
                If i = 3 Then
                    a = 7
                Else
                    a = 6
                End If
            End If
        End If

    End Sub

End Module

.method public static void  Main() cil managed
{
  .entrypoint
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       30 (0x1e)
  .maxstack  2
  .locals init ([0] int32 a,
           [1] int32 i)
  IL_0000:  ldc.i4.1
  IL_0001:  stloc.1
  IL_0002:  ldloc.1
  IL_0003:  ldc.i4.1
  IL_0004:  bne.un.s   IL_000b
  IL_0006:  ldc.i4.s   9
  IL_0008:  stloc.0
  IL_0009:  br.s       IL_001d
  IL_000b:  ldloc.1
  IL_000c:  ldc.i4.2
  IL_000d:  bne.un.s   IL_0013
  IL_000f:  ldc.i4.8
  IL_0010:  stloc.0
  IL_0011:  br.s       IL_001d
  IL_0013:  ldloc.1
  IL_0014:  ldc.i4.3
  IL_0015:  bne.un.s   IL_001b
  IL_0017:  ldc.i4.7
  IL_0018:  stloc.0
  IL_0019:  br.s       IL_001d
  IL_001b:  ldc.i4.6
  IL_001c:  stloc.0
  IL_001d:  ret
} // end of method Module1::Main

我建议使用更容易阅读的那个。

于 2013-08-29T13:50:35.723 回答
7

你在担心错误的事情!!!

您编写的代码不是执行的代码。编译器会修改你的代码结构以进行优化,它在诸如此类的事情上做得很好。也就是说,即使它不执行优化,速度差异也无关紧要。

不要担心“它是否尽可能快?” 而是担心“它是否足够快,并且尽可能可维护(可读)?”。

编译器和处理器非常擅长理解逻辑结构,但肉袋(人)却不是。每当您编写代码时,您都应该尝试确保它尽可能平易近人且易于阅读。如果你发现它的速度慢得让人无法接受,那么你可以开始牺牲可读性来换取性能——但出于偏执而这样做被称为“过早优化”,这是一种使代码无法维护(并最终产生错误)的好方法。

话虽如此,这里有一些指导方针:

  • 具有许多 ifs/else 的方法是一种代码气味(它们具有很高的“圈复杂度”。这表明单个方法会执行很多此操作,这使得阅读、维护、测试和更改变得困难。将您的方法分解为许多较小的方法。您可能仍然会得到一个相对较大的“控制”方法,它决定了要做什么——但将实际执行的任务委托给其他方法。

  • 尽可能减少嵌套。

    如果有导致简单return或退出的情况,请尝试在序列的早期检查它们:(例如if (something) { return; })。

  • 将相关检查组合在一起,并尝试将它们重构为自己的方法

  • 在测试中广泛涵盖所有内容

于 2013-08-29T13:26:39.153 回答
2

好吧,我相信这完全取决于您正在检查的条件。

例如(伪代码):

if (A && B) {
} elseif (A && C) {
} elseif (A && D) {
}

在此示例中,所有语句之间共享一个公共条件if,这意味着重写以下内容可能更有效:

if (A) {
    if (B) {
    } elseif (C) {
    } elseif (D) {
    }
}

但是,如果您缓存A条件的结果。性能提升可能很小。也许编译器甚至会执行优化,因此您必须运行性能测试以确保执行时间甚至存在差异。

更重要的是,除非您正在编写一些对性能至关重要的代码,否则请始终尝试通过关注可读性来编写代码。几乎总有一种有效的方法可以在不影响效率的情况下展平条件语句。

于 2013-08-29T13:23:38.483 回答
0

这取决于您的代码。

if 语句仅在满足条件时才访问,否则将被忽略。if elseif else 块是相同的,但它正在测试许多条件,并且根据满足哪个条件,可能必须执行不同的操作才能获得您想要的结果。

我的意见是“视情况而定”。

如果要执行代码中的所有内容,请使用 elseif..

如果您想忽略某些内容,请使用 if..

于 2013-08-29T13:03:21.633 回答
0

当我有那么多时,我总是更喜欢开关盒if elseif,但我知道这总是可能的。在那种情况下,ElseIf总是看起来更好,并且在后台实现else if,所以它应该是相同的性能。

但!对于很多人,包括我的一些同事和老板来说,它是不可读的,因为它读为if if if. 我知道这很疯狂,但我认为这是一些心理学问题……所以我明白你的同事来自哪里

于 2013-08-29T13:03:42.243 回答
0

我进行了快速测试,发现ElseIf 的运行速度比 Nested If 稍快。请参阅下面的代码。

Imports System.Diagnostics
Module Module1

    Sub Main()
        Dim sw As New Stopwatch()
        Dim nestedTotal As Integer
        sw.Start()
        For i = 1 To 100000
            Nested()
        Next
        sw.Stop()
        nestedTotal = sw.ElapsedMilliseconds

        sw.Reset()

        Dim elsesTotal As Integer
        sw.Start()
        For i = 1 To 100000
            Elses()
        Next
        sw.Stop()
        elsesTotal = sw.ElapsedMilliseconds
        Console.WriteLine("Nested If:" & nestedTotal)
        Console.WriteLine("ElseIf:" & elsesTotal)
        Console.Read()
    End Sub

    Sub Nested()
        Dim num As Integer = GetNum()
        If num = 1 Then
            DoSomething()
        Else
            If num = 2 Then
                DoSomething()
            Else
                If num = 3 Then
                    DoSomething()
                Else
                    If num = 4 Then
                        DoSomething()
                    Else
                        DoSomething()
                    End If
                End If
            End If
        End If
    End Sub

    Sub DoSomething()
        Dim j As Integer
        For i = 1 To 1000
            j = i + j
        Next
    End Sub

    Sub Elses()
        Dim num As Integer = GetNum()
        If num = 1 Then
            DoSomething()
        ElseIf num = 2 Then
            DoSomething()
        ElseIf num = 3 Then
            DoSomething()
        ElseIf num = 4 Then
            DoSomething()
        Else
            DoSomething()
        End If
    End Sub

    Function GetNum()
        Dim Generator As System.Random = New System.Random()
        Return Generator.Next(1, 5)
    End Function
End Module
于 2013-08-29T13:20:44.393 回答
0

从性能的角度来看,没有有意义的差异。对我来说,ElseIf 的可读性显然更好。

Private Sub xelseif(tries As Integer)
    Dim foo As Integer
    For x As Integer = 1 To tries
        For y As Integer = 1 To 5 Step 4
            If y = 1 Then
                foo = y
            ElseIf y = 2 Then
            ElseIf y = 3 Then
            ElseIf y = 4 Then
            ElseIf y = 5 Then
                foo = y
            End If
        Next
    Next
End Sub

Private Sub xelse(tries As Integer)
    Dim foo As Integer
    For x As Integer = 1 To tries
        For y As Integer = 1 To 5 Step 4
            If y = 1 Then
                foo = y
            Else
                If y = 2 Then
                Else
                    If y = 3 Then
                    Else
                        If y = 4 Then
                        Else
                            If y = 5 Then
                                foo = y
                            End If
                        End If
                    End If
                End If
            End If
        Next
    Next
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim stpw As New Stopwatch
    Dim tries As Integer = 1500000

    xelse(10)
    stpw.Restart()
    xelse(tries)
    stpw.Stop()
    Debug.WriteLine(stpw.ElapsedMilliseconds)

    xelseif(10)
    stpw.Restart()
    xelseif(tries)
    stpw.Stop()
    Debug.WriteLine(stpw.ElapsedMilliseconds)
End Sub
于 2013-08-29T13:52:46.333 回答