ByRef vs ByVal澄清

时间:2010-12-08 00:38:57

标签: 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 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

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

现在,在我的Socket媒体资源上,设置值时,必须为ByVal。据我所知,内存中的实例复制的,而这个新实例被传递给value,我的代码集_Socket在内存中引用此实例。是

如果这是真的,那么我不明白为什么我会想要使用除了本机类型之外的任何属性。我想如果复制具有大量成员的类实例会有相当大的性能损失。此外,特别是对于这个代码,我想象一个复制的套接字实例不会真正起作用,但我还没有测试过它。

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

4 个答案:

答案 0 :(得分:47)

我认为你混淆了引用与价值类型的概念以及ByValByRef的混淆。虽然他们的名字有点误导,但它们是正交问题。

VB.NET中的

ByVal表示将提供的值的副本发送到该函数。对于值类型(IntegerSingle等),这将提供值的浅表副本。对于较大的类型,这可能是低效的。对于引用类型(String,类实例),传递引用的副本。因为副本通过=以突变方式传递给参数,所以调用函数不会看到它。

VB.NET中的

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

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

(1)这不是100%正确,因为VB.NET实际上在调用点支持几种ByRef。有关详细信息,请参阅博客条目 The many cases of ByRef


答案 1 :(得分:12)

请记住, 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

答案 2 :(得分:3)

我的理解一直是ByVal / ByRef决定对于值类型(在堆栈上)最重要。 ByVal / ByRef对引用类型(在堆上)几乎没有什么区别,除非引用类型是immutable,如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

答案 3 :(得分:1)

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

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

通过值传递a。 传递a1是对a的引用;它是一个地址。 传递a2是对参考的引用;传递的是a1的地址。

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

的实例