22

我刚刚开始学习一个类来处理与 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

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

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

如果这是真的,那么我不明白为什么我想将属性用于除本机类型之外的任何东西。我想如果复制具有大量成员的类实例,性能会受到很大影响。此外,特别是对于这段代码,我想复制的套接字实例不会真正起作用,但我还没有测试过。

无论如何,如果你能证实我的理解,或者解释我模糊逻辑中的缺陷,我将不胜感激。

4

4 回答 4

54

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

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

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

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

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


于 2010-12-08T00:45:58.207 回答
14

请记住,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
于 2010-12-08T01:20:10.487 回答
2

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

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
于 2010-12-08T01:31:46.243 回答
1

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

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

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

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

于 2018-04-23T17:06:32.870 回答