Vb.net ByRef vs ByVal澄清
我刚刚开始使用一个类来处理到TCP服务器的客户端连接。以下是我迄今为止编写的代码:Vb.net ByRef vs ByVal澄清,vb.net,byref,byval,Vb.net,Byref,Byval,我刚刚开始使用一个类来处理到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
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
因此,在我的重载构造函数中,我接受对系统.Net.Sockets.Socket
实例的引用,是吗
现在,在我的Socket
属性上,设置值时,它必须是ByVal
。我的理解是,复制内存中的实例,并将此新实例传递给值
,我的代码将\u Socket
设置为在内存中引用此实例。是吗
如果这是真的,那么我就不明白为什么我要对本机类型以外的任何东西使用属性。我可以想象,如果复制包含大量成员的类实例,会对性能造成相当大的影响。另外,特别是对于这段代码,我认为复制的套接字实例不会真正起作用,但我还没有测试它
无论如何,如果你能证实我的理解,或者解释我模糊逻辑中的缺陷,我将不胜感激。我认为你混淆了引用与值类型的概念,以及
ByVal
与ByRef
的概念。尽管它们的名字有点误导,但它们是相互正交的问题
VB.NET中的ByVal
表示将向函数发送所提供值的副本。对于值类型(Integer
、Single
等),这将提供值的浅拷贝。对于较大的类型,这可能是低效的。但对于引用类型(String
,类实例),会传递引用的副本。由于副本通过=
传递给参数,因此调用函数将看不到该副本
VB.NET中的ByRef
表示将向函数(1)发送对原始值的引用。这几乎就像原始值直接在函数中使用一样。像=
这样的操作将影响原始值,并在调用函数中立即可见
Socket
是一种引用类型(read类),因此用ByVal
传递它很便宜。即使它确实执行了复制,但它是引用的副本,而不是实例的副本
(1) 但这并不是100%正确,因为VB.NET实际上在调用站点上支持几种ByRef。有关更多详细信息,请参阅博客条目
记住,
ByVal
仍然传递引用。不同之处在于您获得了引用的副本
因此,在重载构造函数上,我接受对System.Net.Sockets.Socket实例的引用,是吗
是的,但是如果你要求它ByVal
,情况也是一样的。不同之处在于,使用ByVal
可以获得引用的副本—您有了新的变量。对于ByRef
,它是相同的变量
据我所知,内存中的实例是复制的
没有。仅复制引用。因此,您仍在使用同一实例
下面是一个代码示例,它更清楚地解释了这一点:
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
我的理解一直是,ByVal/ByRef决策对值类型(在堆栈上)最为重要。ByVal/ByRef对引用类型(在堆上)几乎没有什么区别,除非该引用类型类似于System.String。对于可变对象,传递对象ByRef或ByVal并不重要,如果在方法中对其进行修改,调用函数将看到修改 套接字是可变的,所以您可以通过任何方式传递,但如果您不想保留对对象的修改,则需要自己进行深度复制
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
想想C,看看标量(比如int)和int指针以及指向int指针的指针之间的区别
int a;
int* a1 = &a;
int** a2 = &a1;
通过值传递。
传递a1是对a的引用;这是a的地址。
传递a2是对引用的引用;传递的是a1的地址
使用ByRef传递列表变量类似于a2场景。它已经是一个参考。您正在将引用传递给引用。这样做意味着不仅可以更改列表的内容,还可以更改参数以指向完全不同的列表。这还意味着您不能传递文本null而不是List的实例参见(以及我在上面的一些行中的注释):--ByRef是“通过引用调用”(简而言之:“可以分配给变量并使其影响调用方传递的变量”),ByVal是“通过值调用”。在传递引用的值时,您仍然可以对通过ByVal传递的引用类型进行变异(对象本身不会发生复制/克隆/复制)。+1-“我认为您混淆了引用与值类型以及ByVal与ByRef的概念。尽管这些名称有点误导,但它们是正交问题。“C++产生了这样的问题。当我传递一个空引用时,我只传递引用类型,引用一个过程,在这个过程中,我实例化一个要传递回调用过程的对象的新实例。您可以通过先实例化对象的新实例,然后将其传递给相同的过程(但这次是ByVal)来实现同样的目的。当引用的博客文章被移动时,您博客中的信息应该按值发布(链接更新)。极端概要:VB自动添加临时变量,并将值复制到临时变量中,有时从临时变量中复制值,以支持无法直接使用ByRef的情况,例如传递常量值、属性或后期绑定调用。请注意。。。你的Baz变量并不像你想象的那样安全。将这两个方法中的代码都更改为
Baz.Bar=“replaced”
,MyFoo变量将同时使用ByVal和ByRef版本。一个让许多VB开发人员头疼的bug。如果您的Bar属性只有一个getter,那么它将是不可变的,您将是安全的。就目前的情况而言,代码i