Question

Reading Which is faster? ByVal or ByRef? made me wonder whether the comments in there did apply to Strings in terms of performance. Since strings are copied before being passed, isn't it much more efficient (if the callee doesn't need a copy of string course) to pass strings ByRef?

Thanks,
CFP.

Edit: Consider this code, which made me think there was some kind of copy going on:

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

It outputs:

ByVal: Hello World (ByVal)!
ByRef: foobar
Was it helpful?

Solution

Strings are not copied before being passed. Strings are reference types, even though they behave somewhat like value types.

You should use whatever makes the most sense in the context of your requirements. (And if your requirements happen to be something like "must squeeze every last nanosecond of performance at the expense of all other considerations" then you should probably crack out the profiler rather than asking on stackoverflow!)

This is almost certainly something that you don't need to worry about, and I doubt if there's ever a significant performance difference. The only situation where I can see any chance of a difference would be when passing big value types.

OTHER TIPS

I decided to check this for myself, to get a more "scientific" answer. They are the same. If I use the code below, ByVal is about 2% slower than ByRef. If, however, I swap them around, so that I'm timing ByRef before ByVal, then ByRef is about 2% slower. So, what actually matters more than ByRef or ByVal in this case is the order in which they run :)

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

To understand the behavior of class-types, including strings, regard all class-type parameters, variables, fields, and array elements, etc. as holding "object ids". If Foo is a variable of type string, the statement Foo = 12345.ToString(); will create a new object id (hypothetically, Object ID#197), and create a new object of type string with that id, holding the five characters "12345". It will then store Object ID#197 into the variable Foo. If one calls a routine with a non-ref parameter param, and passes Foo to it, then param will be a local variable holding Object ID #197. The statement param += "6"; would create a new object (e.g. Object ID #521), of type string, holding the six characters "123456" and store Object ID #521 into param. Note that Foo still holds Object ID#197, and that object still holds the five-character string "12345".

If param had been passed by ref, then the statement param += "6" would have stored Object ID #521 into Foo. It still would not have caused any observable change to Object #197, except perhaps to make it eligible for garbage collection (if Foo had been the only reference to Object #197, overwriting it would mean that there would no longer exist any reference to that object anywhere in the universe).

Note that it's generally pretty easy to reason about immutable class types like string, even without thinking in terms of Object IDs, since the only way to change the sequence of characters represented by a string variable is to store a different Object ID there. Thinking in terms of Object IDs becomes essential, however, when dealing with mutable class types. Passing a variable of class type Car, not by ref, would be equivalent to copying a VIN from one slip of paper to another, and handing the latter slip of paper to some shop workers, and asking them to do something with it. If the first paper originally identified a red car with VIN#15934, then when the workers were done the first paper might identify a blue car with VIN#15934, but it would be the same car. Nothing the workers could do with the slip of paper they were given, nor anything they could do with the car, would change which car the first paper referred to. On the other hand, passing the parameter by reference would be more like the shop workers a piece of paper with the VIN written on it, and getting the paper back from them when they were done. If the workers could cross out the VIN and write another, then when they returned the slip of paper it might refer to the same car or a different car; if it refers to a different car, the car to which it originally referred may or may not have been modified, and the car to which the paper ends up referring may or may not bear any resemblance to the original.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top