Reputation: 12937
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
Upvotes: 4
Views: 16264
Reputation: 81217
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.
Upvotes: 0
Reputation: 5385
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
Upvotes: 3
Reputation: 269498
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.
Upvotes: 10