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
ByRef vs ByVal Clarification
提问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 Socket
property, 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 _Socket
to 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 ByVal
vs. ByRef
. Even though their names are a bit misleading, they are orthogonal issues.
我认为您混淆了引用与值类型以及ByVal
与ByRef
. 尽管它们的名字有点误导,但它们是正交的问题。
ByVal
in 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 中意味着所提供值的副本将被发送到该函数。对于值类型(Integer
、Single
等),这将提供值的浅拷贝。对于较大的类型,这可能效率低下。对于引用类型 ( String
, 类实例),传递了引用的副本。因为一个副本是通过=
它在参数的变化中传递的,它对调用函数是不可见的。
ByRef
in 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=
将影响原始值并在调用函数中立即可见。
Socket
is a reference type (read class) and hence passing it with ByVal
is 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 ByVal
still 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 ByVal
instead. The difference is that with ByVal
you 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 的实例