vb.net ByRef 与 ByVal 的澄清

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4383167/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 15:11:02  来源:igfitidea点击:

ByRef vs ByVal Clarification

vb.netbyrefbyval

提问by Brad

I'm just starting on a class to handle client connections to a TCP server. Here is the code I've written thus far:

我刚刚开始学习处理客户端与 TCP 服务器的连接的课程。这是我迄今为止编写的代码:

Imports System.Net.Sockets
Imports System.Net

Public Class Client
    Private _Socket As Socket

    Public Property Socket As Socket
        Get
            Return _Socket
        End Get
        Set(ByVal value As Socket)
            _Socket = value
        End Set
    End Property

    Public Enum State
        RequestHeader ''#Waiting for, or in the process of receiving, the request header
        ResponseHeader ''#Sending the response header
        Stream ''#Setup is complete, sending regular stream
    End Enum

    Public Sub New()

    End Sub

    Public Sub New(ByRef Socket As Socket)
        Me._Socket = Socket

    End Sub
End Class

So, on my overloaded constructor, I am accepting a referenceto an instanceof a System.Net.Sockets.Socket, yes?

因此,在我的重载构造函数中,我接受了对 a实例引用,是吗?System.Net.Sockets.Socket

Now, on my Socketproperty, when setting the value, it is required to be ByVal. It is my understanding that the instancein memory is copied, and this new instanceis passed to value, and my code sets _Socketto reference this instance in memory. Yes?

现在,在我的Socket财产上,在设置值时,它必须是ByVal. 我的理解是复制内存中的实例,并将这个新实例传递给,我的代码设置为引用内存中的这个实例。是的?value_Socket

If this is true, then I can't see why I would want to use properties for anything but native types. I'd imagine there can be quite a performance hit if copying class instances with lots of members. Also, for this code in particular, I'd imagine a copied socket instance wouldn't really work, but I haven't tested it yet.

如果这是真的,那么我不明白为什么我想将属性用于本机类型以外的任何内容。我想如果复制具有大量成员的类实例可能会对性能造成很大影响。此外,特别是对于这段代码,我认为复制的套接字实例不会真正起作用,但我还没有对其进行测试。

Anyway, if you could either confirm my understanding, or explain the flaws in my foggy logic, I would greatly appreciate it.

不管怎样,如果你能证实我的理解,或者解释我模糊逻辑的缺陷,我将不胜感激。

回答by JaredPar

I think you're confusing the concept of references vs. value types and ByValvs. ByRef. Even though their names are a bit misleading, they are orthogonal issues.

我认为您混淆了引用与值类型以及ByValByRef. 尽管它们的名字有点误导,但它们是正交的问题。

ByValin VB.NET means that a copy of the provided value will be sent to the function. For value types (Integer, Single, etc.) this will provide a shallow copy of the value. With larger types this can be inefficient. For reference types though (String, class instances) a copy of the reference is passed. Because a copy is passed in mutations to the parameter via =it won't be visible to the calling function.

ByVal在 VB.NET 中意味着所提供值的副本将被发送到该函数。对于值类型(IntegerSingle等),这将提供值的浅拷贝。对于较大的类型,这可能效率低下。对于引用类型 ( String, 类实例),传递了引用的副本。因为一个副本是通过=它在参数的变化中传递的,它对调用函数是不可见的。

ByRefin VB.NET means that a reference to the original value will be sent to the function (1). It's almost like the original value is being directly used within the function. Operations like =will affect the original value and be immediately visible in the calling function.

ByRef在 VB.NET 中意味着对原始值的引用将被发送到函数 (1)。这几乎就像在函数中直接使用原始值一样。操作 like=将影响原始值并在调用函数中立即可见。

Socketis a reference type (read class) and hence passing it with ByValis cheap. Even though it does perform a copy it's a copy of the reference, not a copy of the instance.

Socket是一个引用类型(读取类),因此传递它ByVal很便宜。即使它确实执行了副本,它也是引用的副本,而不是实例的副本。

(1) This is not 100% true though because VB.NET actually supports several kinds of ByRef at the callsite. For more details, see the blog entry The many cases of ByRef

(1) 这不是 100% 正确,因为 VB.NET 实际上在调用站点支持多种 ByRef。更多详情请看博客文章ByRef 的许多案例



回答by Joel Coehoorn

Remember that ByValstill passes references.The difference is that you get a copy of the reference.

请记住,ByVal仍然通过引用。不同之处在于您会获得参考的副本。

So, on my overloaded constructor, I am accepting a reference to an instance of a System.Net.Sockets.Socket, yes?

因此,在我的重载构造函数中,我接受了对 System.Net.Sockets.Socket 实例的引用,是吗?

Yes, but the same would be true if you asked for it ByValinstead. The difference is that with ByValyou get a copy of the reference — you have new variable. With ByRef, it's the same variable.

是的,但如果您要求它,情况也是如此ByVal。不同之处在于,当ByVal您获得引用的副本时,您就有了新变量。与ByRef,它是相同的变量。

It is my understanding that the instance in memory is copied

我的理解是复制内存中的实例

Nope. Only the reference is copied. Therefore, you're still working with the same instance.

不。仅复制引用。因此,您仍在使用同一个实例。

Here's a code example that explains it more clearly:

这是一个更清楚地解释它的代码示例:

Public Class Foo
   Public Property Bar As String
   Public Sub New(ByVal Bar As String)
       Me.Bar = Bar
   End Sub
End Class

Public Sub RefTest(ByRef Baz As Foo)
     Baz.Bar = "Foo"
     Baz = new Foo("replaced")
End Sub

Public Sub ValTest(ByVal Baz As Foo)
    Baz.Bar = "Foo"
    Baz = new Foo("replaced")
End Sub

Dim MyFoo As New Foo("-")
RefTest(MyFoo)
Console.WriteLine(MyFoo.Bar) ''# outputs replaced

ValTest(MyFoo)
Console.WriteLine(MyFoo.Bar) ''# outputs Foo

回答by mattmc3

My understanding has always been that the ByVal/ByRef decision really matters most for value types (on the stack). ByVal/ByRef makes very little difference at all for reference types (on the heap) UNLESS that reference type is immutablelike System.String. For mutable objects, it doesn't matter if you pass an object ByRef or ByVal, if you modify it in the method the calling function will see the modifications.

我的理解一直是 ByVal/ByRef 决定对于值类型(在堆栈上)确实最重要。ByVal/ByRef 对于引用类型(在堆上)几乎没有什么区别,除非引用类型像 System.String 一样是不可变的。对于可变对象,传递对象是 ByRef 还是 ByVal 都没有关系,如果在方法中修改它,调用函数将看到修改。

Socket is mutable, so you can pass any which way you want, but if you don't want to keep modifications to the object you need to make a deep copy yourself.

Socket 是可变的,因此您可以通过任何您想要的方式传递,但是如果您不想保留对对象的修改,则需要自己进行深度复制。

Module Module1

    Sub Main()
        Dim i As Integer = 10
        Console.WriteLine("initial value of int {0}:", i)
        ByValInt(i)
        Console.WriteLine("after byval value of int {0}:", i)
        ByRefInt(i)
        Console.WriteLine("after byref value of int {0}:", i)

        Dim s As String = "hello"
        Console.WriteLine("initial value of str {0}:", s)
        ByValString(s)
        Console.WriteLine("after byval value of str {0}:", s)
        ByRefString(s)
        Console.WriteLine("after byref value of str {0}:", s)

        Dim sb As New System.Text.StringBuilder("hi")
        Console.WriteLine("initial value of string builder {0}:", sb)
        ByValStringBuilder(sb)
        Console.WriteLine("after byval value of string builder {0}:", sb)
        ByRefStringBuilder(sb)
        Console.WriteLine("after byref value of string builder {0}:", sb)

        Console.WriteLine("Done...")
        Console.ReadKey(True)
    End Sub

    Sub ByValInt(ByVal value As Integer)
        value += 1
    End Sub

    Sub ByRefInt(ByRef value As Integer)
        value += 1
    End Sub

    Sub ByValString(ByVal value As String)
        value += " world!"
    End Sub

    Sub ByRefString(ByRef value As String)
        value += " world!"
    End Sub

    Sub ByValStringBuilder(ByVal value As System.Text.StringBuilder)
        value.Append(" world!")
    End Sub

    Sub ByRefStringBuilder(ByRef value As System.Text.StringBuilder)
        value.Append(" world!")
    End Sub

End Module

回答by ChrisG65

Think of C, and the difference between a scalar, like int, and an int pointer, and a pointer to an int pointer.

想想 C,以及标量(如 int)和 int 指针以及指向 int 指针的指针之间的区别。

int a;
int* a1 = &a;
int** a2 = &a1;

Passing a is by value. Passing a1 is a reference to a; it is the address of a. Passing a2 is a reference to a reference; what is passed is the address of a1.

传递 a 是按值传递的。传递 a1 是对 a 的引用;它是a的地址。传递 a2 是对引用的引用;传递的是a1的地址。

Passing a List variable using ByRef is analogous to the a2 scenario. It is already a reference. You are passing a reference to a reference. Doing that means that not only can you change the contents of the List, you can can change the parameter to point to an entirely different List. It also means you can not pass a literal null instead of an instance of List

使用 ByRef 传递 List 变量类似于 a2 场景。它已经是一个参考。您正在传递对引用的引用。这样做意味着您不仅可以更改 List 的内容,还可以更改参数以指向一个完全不同的 List。这也意味着您不能传递文字 null 而不是 List 的实例