传递字符串时ByRef vs ByVal的性能

时间:2010-07-22 11:03:36

标签: vb.net performance string byref byval

阅读Which is faster? ByVal or ByRef?让我想知道那里的评论是否适用于Strings的表现。由于字符串在传递之前被复制,因此传递字符串ByRef是不是更有效(如果被调用者不需要字符串课程的副本)?

谢谢,
CFP。

修改:考虑一下这段代码,这让我觉得有一些副本正在进行:

Sub Main()
    Dim ByValStr As String = "Hello World (ByVal)!"
    Dim ByRefStr As String = "Hello World (ByRef)!"

    fooval(ByValStr)
    fooref(ByRefStr)

    Console.WriteLine("ByVal: " & ByValStr)
    Console.WriteLine("ByRef: " & ByRefStr)

    Console.ReadLine()
End Sub


Sub fooval(ByVal Str As String)
    Str = "foobar"
End Sub

Sub fooref(ByRef Str As String)
    Str = "foobar"
End Sub

输出:

ByVal: Hello World (ByVal)!
ByRef: foobar

3 个答案:

答案 0 :(得分:9)

字符串在传递之前未被复制。字符串是引用类型,即使它们的行为有点像值类型。

您应该根据您的要求使用最有意义的内容。 (如果你的要求碰巧是“必须以牺牲所有其他考虑因素为代价挤压每一秒的性能”,那么你应该破解探查器而不是询问堆栈溢出!)

这几乎肯定是你不必担心的事情,我怀疑是否存在显着的性能差异。我可以看到任何差异的唯一情况是传递 big 值类型。

答案 1 :(得分:2)

我决定自己检查一下,以获得更“科学”的答案。他们是一样的。如果我使用下面的代码,ByVal比ByRef慢约2%。但是,如果我将它们交换掉,那么我在ByVal之前计时ByRef,那么ByRef会慢大约2%。因此,在这种情况下,实际上比ByRef或ByVal更重要的是它们运行的​​顺序:)

Function CreateString()

    Dim i As Integer
    Dim str As String = ""

    For i = 1 To 10000
        str = str & "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    Next i

    Return str
End Function

Sub fooval(ByVal Str As String)
    Str = Str & "foobar"
End Sub

Sub fooref(ByRef Str As String)
    Str = Str & "foobar"
End Sub

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    Dim str As String = CreateString()
    Dim stopWatch As New Stopwatch
    Dim i As Integer

    stopWatch.Start()
    For i = 1 To 1000
        fooval(str)
    Next i
    stopWatch.Stop()
    Dim valtime As Long = stopWatch.ElapsedMilliseconds

    stopWatch.Restart()
    For i = 1 To 1000
        fooref(str)
    Next i
    stopWatch.Stop()
    Dim reftime As Long = stopWatch.ElapsedMilliseconds

    MsgBox("Val took " & valtime & " milliseconds and Ref took " & reftime & " milliseconds")
End Sub

答案 2 :(得分:0)

要理解类类型(包括字符串)的行为,请将所有类类型参数,变量,字段和数组元素等视为保存“对象ID”。如果Foostring类型的变量,则语句Foo = 12345.ToString();将创建新的对象ID(假设为对象ID#197),并创建类型为{{1}的新对象使用该ID,持有五个字符string。然后,它会将"12345"存储到变量Object ID#197中。如果使用非ref参数Foo调用例程并将param传递给它,那么Foo将是一个持有param的局部变量。语句Object ID #197将创建一个类型为字符串的新对象(例如,对象ID#521),其中包含六个字符param += "6";并将"123456"存储到Object ID #521中。请注意,param仍然保留Foo,该对象仍保留五个字符的字符串Object ID#197

如果"12345"已通过param,则语句ref会将param += "6"存储到Object ID #521。它仍然不会对Object#197造成任何可观察的更改,除非可能使其符合垃圾收集条件(如果Foo是对象#197的唯一引用,则覆盖它将意味着不再存在在宇宙中任何地方对该对象的任何引用。)

请注意,即使不考虑对象ID,通常也很容易推理不可变的类类型,如Foo,因为更改字符串变量表示的字符序列的唯一方法是存储那里有不同的Object ID。然而,在处理可变类类型时,根据对象ID进行思考变得至关重要。传递类型string的变量,而不是参考,将相当于将VIN从一张纸复制到另一张纸,并将后一张纸交给一些商店工人,并要求他们做一些事情。它。如果第一篇论文最初确定了一辆带有VIN#15934的红色汽车,那么当工人完成后,第一篇论文可能会识别出一辆VIN#15934的蓝色汽车,但它将是同一辆汽车。工人们没有用纸张给他们做的任何事情,也没有任何他们能用汽车做的事情,会改变第一篇论文提到的那辆车。另一方面,通过引用传递参数将更像是商店工作人员在纸上写上VIN的纸张,并在完成后从纸张上取回纸张。如果工人可以划掉VIN并写下另一个,那么当他们退回纸条时,它可能会指的是同一辆车或另一辆车;如果它指的是不同的汽车,它最初提到的汽车可能已经或可能没有被修改过,而该汽车最终引用的汽车可能会或可能不会与原件相似。