ByVal 表现得像 ByRef

ByVal behaving like ByRef

也许这是 Visual Studio 2022(当前版本)中的错误... 但根据我到目前为止对 ByVal 和 ByRef 的了解,以及这个文档:

https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/procedures/passing-arguments-by-value-and-by-reference#:~:text=The%20advantage%20of%20passing%20an,being%20changed%20by%20the%20procedure.

... If the underlying element is modifiable, but you do not want the procedure to be able to change its value, declare the parameter ByVal. Only the calling code can change the value of a modifiable element passed by value. ...

我希望这个代码:

Friend Sub Driver()

    Dim index As Integer = 5

    Dim OneDimensionalMatrix(index) As Integer

    For x As Integer = 0 To index - 1
        OneDimensionalMatrix(x) = 44
    Next

    SubRoutine(index, OneDimensionalMatrix)

    Console.WriteLine(index)

    Console.WriteLine(OneDimensionalMatrix(3))

End Sub

Public Sub SubRoutine(ByVal matrixSize As Integer, ByVal OneDimensionalMatrix() As Integer)

    For x As Integer = 0 To matrixSize - 1

        OneDimensionalMatrix(x) = 55

    Next

    matrixSize = 100

End Sub

将生成以下输出:

5

44

但它生成的输出为:

5

55

这对我来说意味着 OneDimensionalMatrix 的 ByVal 被当作 ByRef 声明来处理。

我是不是遗漏了什么,或者这是 VS 2022 中的错误?

以下是按值或按引用传递的非常简单的规则:

  1. 按值传递值类型(结构)时,修改对象或分配新对象都不会影响原始变量。
  2. 通过引用传递值类型时,修改对象和分配新对象都会影响原始变量。
  3. 按值传递引用类型 (class) 时,修改对象会影响原始变量,但分配新对象不会。
  4. 通过引用传递引用类型时,修改对象和分配新对象都会影响原始变量。

如果不修改影响原始变量的对象,则无法传递引用类型对象。如果那是您想要的,那么您可以显式创建对象的副本并将其传递给方法。

按值传递方法参数与分配给另一个变量完全一样。它创建原始变量内容的副本。如果该内容是一个值,则复制该值,如果该内容是一个引用,则复制该引用。如果你复制引用,那么你有两个对同一个对象的引用,而不是两个对象。你希望在这里输出什么:

Dim arr1 = {1, 2, 3}
Dim arr2 = arr1

arr2(1) = 5

Console.WriteLine(arr1(1))

希望你说“5”,因为希望你意识到 arr1arr2 指的是同一个数组对象。当您按值将数组或任何引用类型对象传递给方法时,情况也是如此。

这种行为正是为什么 ByVal 在 VB.NET 中是默认值,而在 VB6 中是 ByRef。在 VB6 中,默认复制大对象,因此您必须通过引用传递以避免这种情况。在 VB.NET 中,默认情况下不会复制大对象,所以你只通过引用传递,实际上你想传出一个与传入的对象不同的对象。默认情况下复制值类型的事实是主要的原因之一值类型应该永远很小的原因。我相信建议不大于32字节,即最多8个字段。