.net - visual - Mejor práctica: ByRef o ByVal? en la red
byref c# (11)
¿Cuáles son las cosas a considerar al elegir entre ByRef y ByVal?
Entiendo la diferencia entre los dos, pero no entiendo completamente si ByRef ahorra recursos o si incluso tenemos que preocuparnos por eso en el entorno .Net.
¿Cómo se decide entre los dos si la funcionalidad no tiene importancia en una situación?
ByVal debe ser tu "predeterminado". Úselo a menos que tenga un motivo específico para usar ByRef
El valor predeterminado es byValue para TODOS los tipos, pero es importante entender qué significan las dos opciones para un "tipo de referencia" (una clase) en comparación con un tipo de valor. (estructuras)
Para un tipo de referencia, si declara una variable de tipo de referencia en un método, esa variable es una ubicación de memoria en el marco de pila del método. No está en el montón. Cuando inicializa esa variable (usando nuevo o una fábrica, lo que sea), entonces ha creado un objeto real en el montón, y la dirección de ese objeto se almacena en la variable de referencia declarada en su marco de pila de métodos.
Cuando pasa un tipo de referencia a otro método de Val, está creando una copia de la dirección almacenada en la pila de métodos de llamada y pasando la copia de ese valor (la dirección del puntero) al método llamado, donde se almacena en una memoria nueva ranura en la pila de métodos llamados. Dentro del método llamado, la nueva variable clonada apunta directamente al mismo objeto en el Heap. Entonces usarlo puede cambiar las propiedades del mismo objeto. Pero no puede cambiar a qué objeto de pila apunta la variable de referencia original (en la pila de métodos de llamada). Si, en el método llamado escribo
myVar = new object();
La variable original en el método de llamada no habrá cambiado para apuntar a un nuevo objeto.
Si paso un tipo de referencia por Ref, otoh, estoy pasando un puntero a la variable declarada en la pila de métodos de llamada (que contiene un puntero al objeto en el montón) Por lo tanto, es un puntero a un puntero al objeto. Apunta a la ubicación de la memoria en la pila de métodos de llamada, que apunta al objeto en el montón.
Así que ahora, si cambio el valor de la variable en el método llamado, estableciéndolo en un nuevo objeto (), como arriba, dado que es una referencia a la variable en el método de llamada, en realidad estoy cambiando qué objeto la variable en el método de llamada está apuntando a. Entonces, después de que el método llamado regrese, la variable en el método de llamada ya no estará apuntando al mismo objeto original en el montón.
Hay mucha desinformación sobre esto. Lo principal es que comprenda la diferencia entre los tipos de valores y los tipos de referencia , y la diferencia entre pasar por valor y pasar por referencia .
Casi siempre quieres pasar de valor. Pasar por referencia es casi siempre para "Quiero devolver más de un resultado, y no solo agregando elementos a una lista que se transfiere". El ejemplo clásico de un método que utiliza pass-by-reference es Int32.TryParse donde el valor de retorno es un éxito / error, y el valor analizado es "devuelto" por un parámetro out.
Pasar un objeto ByVal en .net no hace una copia del objeto y no consume más recursos que ByRef, un puntero todavía se pasa a la función. El tiempo de ejecución simplemente asegura que no puede modificar el puntero en su función y devolverle un valor diferente. Aún puede hacer cambios en los valores dentro del objeto y verá esos cambios fuera de la función. Es por eso que ByRef se usa muy raramente. Solo es necesario cuando desea que una función cambie el objeto real que está volviendo; por lo tanto, un parámetro de salida.
Use "ByRef" solo si el parámetro es un parámetro de "salida". De lo contrario, use "ByVal". Usar "ByRef" en parámetros que explícitamente no deberían devolver valores es peligroso y puede generar errores fácilmente.
Marcar ciertos argumentos como ByRef muestra al usuario de su función que la variable asignada a ese argumento ** se modificará. ****
Si usa ByRef para todos los argumentos, no habrá forma de decir qué variables son modificadas por la función, y cuáles solo las leyó. (¡aparte de mirar dentro de la fuente de función!)
Yo diría que ByRef nunca debería usarse, que es una mala práctica. Me gustaría aplicarlo incluso a su caso de uso típico de permitir que una función devuelva múltiples valores (a través de parámetros ByRef). Sería mejor para la función devolver una respuesta estructurada que incluya esos valores de retorno múltiples. Es más claro y más obvio si una función solo devuelve valores a través de su declaración de devolución.
De acuerdo con Microsoft, la elección de ByVal o ByRef puede afectar el rendimiento de valores lo suficientemente grandes (ver Pasar argumentos por valor y por referencia (Visual Basic) ):
Actuación. Aunque el mecanismo de aprobación puede afectar el rendimiento de su código, la diferencia generalmente es insignificante. Una excepción a esto es un tipo de valor pasado ByVal. En este caso, Visual Basic copia todo el contenido de datos del argumento. Por lo tanto, para un tipo de valor grande como una estructura, puede ser más eficiente pasarlo por Ref.
[énfasis añadido].
Sub last_column_process()
Dim last_column As Integer
last_column = 234
MsgBox last_column
trying_byref x:=last_column
MsgBox last_column
trying_byval v:=last_column
MsgBox last_column
End Sub
Sub trying_byref(ByRef x)
x = 345
End Sub
Sub trying_byval(ByRef v)
v = 555
End Sub
Mucha confusión intentaré simplificar. Básicamente tienes 4 opciones:
- Pasar un tipo de valor porVal
- Pasar un tipo de valor por Ref
- Pasar un objeto porVal
- Pasar un objeto por Ref
Algunas personas dicen que nunca deberías usar Ref. Si bien son técnicamente correctos, una cosa es cierta. NUNCA debes usar la palabra nunca . Si está diseñando un sistema desde cero, debe evitarlo a toda costa. Usarlo expone un defecto de diseño. Sin embargo, trabajar en un sistema existente puede no proporcionar tanta flexibilidad para implementar un buen diseño. A veces debes hacer consensos, es decir, usar Ref. Por ejemplo, si puede hacer una corrección en 2 días usando Ref, entonces eso puede ser preferible a reinventar la rueda y demorar una semana en obtener la misma solución para evitar el uso de Ref.
Resumen:
- Usando byVal en un tipo de valor: Pasa un valor a una función. Esta es la forma preferida de diseñar funciones.
- Usar byRef en un tipo de valor: útil para devolver más de un valor de una función. Si está creando una función que necesita devolver más de un valor a un sistema existente, esto puede ser mejor que crear un objeto (y establecer propiedades y eliminar) solo para una función.
- Usar byVal en un objeto: pasa un puntero de un objeto a una función. La función puede modificar el objeto.
- Usar byRef en un objeto: pasa un puntero a un puntero de un objeto a una función. Permite cambiar el objeto al que apunta la persona que llama. Esto puede causar algunos errores difíciles de encontrar y no puedo pensar en ninguna buena razón para usarlo. No quiere decir que no hay uno, pero si lo hay, son pocos y distantes.
Joe tu explicación es bastante clara y muy útil. Gracias :)