ByRef vs ByVal Aclaración
Pregunta
estoy empezando en una clase para controlar las conexiones de cliente a un servidor TCP. Aquí está el código que he escrito hasta ahora:
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
Por lo tanto, en mi constructor sobrecargado, estoy aceptando un referencia para un ejemplo de un System.Net.Sockets.Socket
, sí?
Ahora, en mi propiedad Socket
, al establecer el valor, se requiere ser ByVal
. Es mi entendimiento de que el ejemplo en la memoria es copiados , y esto nueva instancia se pasa a value
, y mis conjuntos de códigos _Socket
hacer referencia a este ejemplo en la memoria. Sí?
Si esto es cierto, entonces no puedo ver por qué me gustaría utilizar las propiedades para cualquier cosa menos tipos nativos. Me imagino que no puede haber un gran impacto en el rendimiento si la copia de instancias de la clase con una gran cantidad de miembros. Además, para este código en particular, me imagino una instancia de toma de copiado no sería realmente el trabajo, pero no he probado todavía.
De todos modos, si se puede confirmar ya sea mi entendimiento, o explicar los defectos en mi lógica de niebla, que sería de gran aprecio.
Solución
Creo que estás confundiendo el concepto de referencias frente a los tipos de valor y ByVal
vs ByRef
. A pesar de que sus nombres están engañando un poco, son cuestiones ortogonales.
ByVal
en medios VB.NET que una copia del valor proporcionado se enviará a la función. Para los tipos de valor (Integer
, Single
, etc.) esto proporcionará una copia superficial del valor. Con tipos más grandes esto puede ser ineficiente. Para los tipos de referencia, aunque (String
, instancias de clases) se pasa una copia de la referencia. Debido a que una copia se pasa en el parámetro mutaciones a través de =
no será visible para la función de llamada.
ByRef
en medios VB.NET que una referencia al valor original se enviará a la función (1). Es casi como el valor original se utiliza directamente en la función. Operaciones como =
afectará el valor original y efectivo de forma inmediata en la función de llamada.
Socket
es un tipo de referencia (clase leer) y por lo tanto pasa con ByVal
es barato. A pesar de que hace realizar una copia es una copia de la referencia, no una copia de la instancia.
(1) Esto no es 100% verdad, porque aunque en realidad VB.NET es compatible con varios tipos de ByRef en el callsite. Para más detalles, consulte la entrada de blog Los numerosos casos de ByRef
Otros consejos
Recuerde que ByVal
todavía pasa referencias. La diferencia es que se obtiene una copia de la referencia.
Por lo tanto, en mi constructor sobrecargado, estoy aceptando una referencia a una instancia de un System.Net.Sockets.Socket, sí?
Sí, pero lo mismo sería cierto si usted pidió que ByVal
lugar. La diferencia es que con ByVal
de obtener una copia de la referencia - usted tiene nueva variable. Con ByRef
, es la misma variable.
Es mi entendimiento de que la instancia en la memoria se copia
Nop. Sólo la referencia se copia. Por lo tanto, usted todavía está trabajando con el misma instancia.
Aquí hay un ejemplo de código que lo explica con mayor claridad:
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
Mi opinión siempre ha sido que el ByVal / ByRef decisión realmente es lo más importante para este tipo de valor (en la pila). ByVal / ByRef hace muy poca diferencia en absoluto para los tipos de referencia (en el montón) a menos que ese tipo de referencia es inmutable como System.String. Para los objetos mutables, no importa si se pasa un objeto ByRef o ByVal, si lo modifica en el método de la función de llamar a ver las modificaciones.
Socket es mutable, lo que puede pasar cualquier manera que desee, pero si usted no desea conservar las modificaciones al objeto que necesita para hacer una copia profunda usted mismo.
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
Think de C, y la diferencia entre un escalar, como int, y un puntero int, y un puntero a un puntero int.
int a;
int* a1 = &a;
int** a2 = &a1;
Pasando a es por valor. a1 Pasando es una referencia a una; es la dirección de una. a2 de aprobación es una referencia a una referencia; lo que se pasa es la dirección de a1.
Al pasar una variable de lista usando ByRef es análogo al escenario A2. Ya es una referencia. Está pasando una referencia a una referencia. Hacer eso significa que no sólo se puede cambiar el contenido de la lista, puede puede cambiar el parámetro a punto a una lista totalmente diferente. También significa que no se puede pasar un nulo literal en lugar de una instancia de List