Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Vb.net ByRef vs ByVal澄清_Vb.net_Byref_Byval - Fatal编程技术网

Vb.net ByRef vs ByVal澄清

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

我刚刚开始使用一个类来处理到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
因此,在我的重载构造函数中,我接受对
系统.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