Best Practice: ByRef or ByVal? in .Net

血红的双手。 提交于 2019-11-30 08:16:58

There's a lot of misinformation around about this. The main thing is that you understand the difference between value types and reference types, and the difference between pass by value and pass by reference.

You almost always want to pass by value. Passing by reference is almost always for "I want to return more than one result, and not just by adding things to a list which is passed in." The classic example of a method using pass-by-reference is Int32.TryParse where the return value is a success/failure, and the parsed value is "returned" by an out parameter.

The default is byValue for ALL types, but it is important to understand what the two options mean for a "reference type" (a class) as opposed to a value type. (structs).

For a reference type, if you declare a reference type variable in a method, that variable is a memory location in the stack frame of the method. It is not on the heap. When you initialize that variable (using new or a factory, whatever), then you have created an actual object on the heap, and the address of that object is stored in the declared reference variable in your methods stack frame.

When you pass a reference type to another method byVal, you are creating a copy of the address stored in the calling methods stack and passing the copy of that value (the pointer address) to the called method, where it is stored in a new memory slot in the called methods stack. Inside the called method, the new cloned variable points directly to the same object on the Heap. So using it can change the properties of the same object. But you cannot change which heap object the original reference variable (on the calling methods stack) points to. If, in the called method I write

  myVar = new object();

The original variable in the calling method will not have changed to point to a new object.

If I pass a reference type byRef, otoh, I am passing a pointer to the declared variable in the calling methods stack (which contains a pointer to the object on the heap) It is therefore a pointer to a pointer to the object. It points to the memory location on the calling methods stack, which points to the object on the heap.
So now, if I change the value of the variable in the called method, by setting it to a new object(), as above, since it is a "refereence" to the variable in the calling method, I am actually changing which object the variable in the calling method is pointing to. So After the called method returns, the variable in the calling method will no longer be pointing to the same original object on the heap.

ByVal should be your "default". Use it unless you have a specific reason to use ByRef

Passing an object ByVal in .net does not make a copy of the object and does not consume more resources then ByRef, A pointer is still passed through to the function. The runtime just ensures that you can't modify the pointer in your function and return a different value for it. You can still make changes to the values within the object and you will see those changes outside the function. That is why ByRef is uses so rarely. It is only needed when you want a function to change the actual object that is coming back; hence an output parameter.

Use "ByRef" only if the parameter is "output" parameter. Otherwise use "ByVal". Using "ByRef" on parameters which explicitly should not return values is dangerous and can easy generate bugs.

I would argue that ByRef should never be used--that it's a bad practice. I would apply that even to its typical use case of allowing a function to return multiple values (via ByRef parameters). It would be better for the function to return a structured response that includes those multiple return values. It's more clear and more obvious if a function only returns values via its return statement.

Marking certain arguments as ByRef shows the user of your function that the variable allocated to that argument **will be modified.****

If you use ByRef for all args, there'll be no way to tell which variables are modified by the function, and which are just read by it. (apart from peeking inside the function source!)

According to Microsoft, choosing ByVal or ByRef can affect performance for large enough values (see Passing Arguments by Value and by Reference (Visual Basic)):

Performance. Although the passing mechanism can affect the performance of your code, the difference is usually insignificant. One exception to this is a value type passed ByVal. In this case, Visual Basic copies the entire data contents of the argument. Therefore, for a large value type such as a structure, it can be more efficient to pass it ByRef.

[emphasis added].

Sub last_column_process()
Dim last_column As Integer

last_column = 234
MsgBox last_column

trying_byref x:=last_column
MsgBox last_column

trying_byval v:=last_column
MsgBox last_column

End Sub

Sub trying_byref(ByRef x)
x = 345
End Sub

Sub trying_byval(ByRef v)
v = 555
End Sub

Lots of confusion I will try to simplify. You basically have 4 choices:

  1. Pass a value type byVal
  2. Pass a value type byRef
  3. Pass an object byVal
  4. Pass an object byRef

Some people say you should never use byRef. While they are technically correct, one thing is for certain. You should NEVER use the word never. If you are designing a system from scratch then byRef should be avoided at all costs. Using it exposes a design flaw. However working on an existing system may not provide as much flexability to implement a good design. Sometimes you must make consessions, i.e. using byRef. For example if you can get a fix in done in 2 days using byRef then that can be preferable to re-inventing the wheel and taking a week to get the same fix in just to avoid using byRef.

Summary:

  1. Using byVal on a value type: Passes a value to a function. This is the preferred way to design functions.
  2. Using byRef on a value type: Useful for returning more than one value from a function. If you are creating a function that needs to return more than one value to an existing system this can be better than creating an object (and setting properties, and disposing) just for one function.
  3. Using byVal on an object: Passes a pointer of an object to a function. The function can modify the object.
  4. Using byRef on an object: Passes a pointer to a pointer of an object to a function. Allows changing the object the caller is pointing to. This can cause some hard to find bugs and I can not think of any good reason to use it. Doesnt mean there isnt one, but if there is they are few and far between.
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!