Estoy creando una función donde necesito pasar un objeto para que pueda ser modificado por la función. Cuál es la diferencia entre:
public void myFunction(ref MyClass someClass)
y
public void myFunction(out MyClass someClass)
¿Cuál debería usar y por qué?
MyClass
que sería unclass
tipo, es decir, un tipo de referencia. En ese caso, el objeto que pasa puede ser modificado pormyFunction
incluso sinref
/out
palabra clave.myFunction
recibirá una nueva referencia que apunta al mismo objeto, y puede modificar ese mismo objeto todo lo que quiera. La diferenciaref
que haría la palabra clave sería quemyFunction
recibiera la misma referencia al mismo objeto. Eso sería importante solo simyFunction
cambiara la referencia para apuntar a otro objeto.Respuestas:
ref
le dice al compilador que el objeto se inicializa antes de ingresar a la función, mientras queout
le dice al compilador que el objeto se inicializará dentro de la función.Entonces, si bien
ref
es bidireccional,out
es exclusivo.fuente
El
ref
modificador significa que:El
out
modificador significa que:fuente
out
, ¿se puede leer en absoluto dentro del método, antes de que se haya establecido con ese método, si se ha inicializado antes de que se llame al método? Quiero decir, ¿puede el método llamado leer lo que el método de llamada le pasó como argumento?Digamos que Dom aparece en el cubículo de Peter sobre la nota sobre los informes de TPS.
Si Dom fuera un argumento de referencia, tendría una copia impresa del memo.
Si Dom fuera una discusión, haría que Peter imprimiera una nueva copia de la nota para que se la llevara.
fuente
Voy a probar suerte con una explicación:
Creo que entendemos cómo funcionan los tipos de valor, ¿verdad? Los tipos de valor son (int, long, struct, etc.). Cuando los envía a una función sin un comando ref, COPIA los datos . Cualquier cosa que haga a esos datos en la función solo afecta a la copia, no al original. El comando ref envía los datos ACTUALES y cualquier cambio afectará los datos fuera de la función.
De acuerdo con la parte confusa, tipos de referencia:
Vamos a crear un tipo de referencia:
Cuando renueva un objeto , se crean dos partes:
Ahora, cuando envía algún objeto a un método sin referencia, COPIA el puntero de referencia , NO los datos. Entonces ahora tienes esto:
Dos referencias apuntando al mismo objeto. Si modifica una propiedad en algún objeto utilizando referencia2, afectará los mismos datos señalados por referencia1.
Si anula la referencia2 o la señala a datos nuevos, no afectará la referencia1 ni la referencia de datos1 a la que apunta.
¿Qué sucede cuando envía un objeto por referencia a un método? La referencia real a algún objeto se envía al método. Entonces ahora solo tiene una referencia a los datos:
Pero ¿qué significa esto? Actúa exactamente igual que enviar algún objeto, no por referencia, excepto por dos cosas principales:
1) Cuando anule la referencia dentro del método, anulará la que está fuera del método.
2) Ahora puede apuntar la referencia a una ubicación de datos completamente diferente y la referencia fuera de la función ahora apuntará a la nueva ubicación de datos.
fuente
ref
yout
parámetros.out
palabra clave?ref está dentro y fuera .
Debería usar
out
con preferencia donde sea suficiente para sus requisitos.fuente
fuera:
En C #, un método solo puede devolver un valor. Si desea devolver más de un valor, puede usar la palabra clave out. El modificador out regresa como retorno por referencia. La respuesta más simple es que la palabra clave "out" se usa para obtener el valor del método.
árbitro:
En C #, cuando pasa un tipo de valor como int, float, double, etc. como argumento al parámetro del método, se pasa por valor. Por lo tanto, si modifica el valor del parámetro, no afecta el argumento en la llamada al método. Pero si marca el parámetro con la palabra clave "ref", se reflejará en la variable real.
fuente
Extendiendo el perro, ejemplo de gato. El segundo método con ref cambia el objeto al que hace referencia la persona que llama. Por lo tanto "Gato" !!!
fuente
Como está pasando un tipo de referencia (una clase), no es necesario usarlo
ref
porque, por defecto, solo se pasa una referencia al objeto real y, por lo tanto, siempre cambia el objeto detrás de la referencia.Ejemplo:
Mientras pase una clase, no tiene que usarla
ref
si desea cambiar el objeto dentro de su método.fuente
someObject = null
paraBar
finalizar la ejecución. Su código se ejecutará bien ya que solo seBar
anuló la referencia a la instancia. Ahora cambiaBar
aBar(ref MyClass someObject)
y ejecutar de nuevo - obtendrá unaNullReferenceException
causaFoo
's referencia a la instancia ha anulado también.ref
yout
comportarse de manera similar, excepto las siguientes diferencias.ref
La variable debe inicializarse antes de su uso.out
variable se puede usar sin asignaciónout
El parámetro debe ser tratado como un valor no asignado por la función que lo utiliza. Entonces, podemos usar elout
parámetro inicializado en el código de llamada, pero el valor se perderá cuando se ejecute la función.fuente
Para aquellos que aprenden con el ejemplo (como yo), esto es lo que dice Anthony Kolesov .
He creado algunos ejemplos mínimos de ref, out y otros para ilustrar el punto. No estoy cubriendo las mejores prácticas, solo ejemplos para entender las diferencias.
https://gist.github.com/2upmedia/6d98a57b68d849ee7091
fuente
"Panadero"
Esto se debe a que el primero cambia su referencia de cadena para que apunte a "Baker". Es posible cambiar la referencia porque la pasó a través de la palabra clave ref (=> una referencia a una referencia a una cadena). La segunda llamada obtiene una copia de la referencia a la cadena.
La cadena parece algo especial al principio. Pero string es solo una clase de referencia y si define
¡entonces s es una referencia a una clase de cadena que contiene el texto "Capaz"! Otra asignación a la misma variable vía
¡no cambia la cadena original, sino que simplemente crea una nueva instancia y señalamos esa instancia!
Puedes probarlo con el siguiente pequeño ejemplo de código:
¿Qué esperas? Lo que obtendrá seguirá siendo "Capaz" porque simplemente establece la referencia en s a otra instancia mientras s2 apunta a la instancia original.
EDITAR: la cadena también es inmutable, lo que significa que simplemente no hay ningún método o propiedad que modifique una instancia de cadena existente (puede intentar encontrar una en los documentos pero no encontrará ninguna :-)). ¡Todos los métodos de manipulación de cadenas devuelven una nueva instancia de cadena! (Es por eso que a menudo obtienes un mejor rendimiento cuando usas la clase StringBuilder)
fuente
ref significa que el valor en el parámetro ref ya está establecido, el método puede leerlo y modificarlo. Usar la palabra clave ref es lo mismo que decir que la persona que llama es responsable de inicializar el valor del parámetro.
out le dice al compilador que la inicialización del objeto es responsabilidad de la función, la función debe asignarse al parámetro out. No está permitido dejarlo sin asignar.
fuente
Fuera: una declaración de devolución se puede usar para devolver solo un valor de una función. Sin embargo, utilizando parámetros de salida, puede devolver dos valores de una función. Los parámetros de salida son como parámetros de referencia, excepto que transfieren datos fuera del método en lugar de hacerlo al mismo.
El siguiente ejemplo lo ilustra:
ref: Un parámetro de referencia es una referencia a una ubicación de memoria de una variable. Cuando pasa parámetros por referencia, a diferencia de los parámetros de valor, no se crea una nueva ubicación de almacenamiento para estos parámetros. Los parámetros de referencia representan la misma ubicación de memoria que los parámetros reales que se proporcionan al método.
En C #, declara los parámetros de referencia utilizando la palabra clave ref. El siguiente ejemplo demuestra esto:
fuente
ref y out funcionan como pasar por referencias y pasar por punteros como en C ++.
Para ref, el argumento debe declararse e inicializarse.
Para salir, el argumento debe declararse pero puede o no inicializarse
fuente
out double Half_nbr
.Tiempo de autoría:
(1) Creamos el método de llamada
Main()
(2) crea un objeto List (que es un objeto de tipo de referencia) y lo almacena en la variable
myList
.Durante el tiempo de ejecución:
(3) El tiempo de ejecución asigna una memoria en la pila en el # 00, lo suficientemente ancho como para almacenar una dirección (# 00 =
myList
, ya que los nombres de las variables son realmente solo alias para ubicaciones de memoria)(4) Runtime crea un objeto de lista en el montón en la ubicación de memoria #FF (todas estas direcciones son, por ejemplo, sake)
(5) Runtime almacenaría la dirección inicial #FF del objeto en # 00 (o en palabras, almacena la referencia del objeto List en el puntero
myList
)Volver al tiempo de autoría:
(6) Luego pasamos el objeto List como argumento
myParamList
al método llamadomodifyMyList
y le asignamos un nuevo objeto ListDurante el tiempo de ejecución:
(7) Runtime inicia la rutina de llamada para el método llamado y, como parte del mismo, verifica el tipo de parámetros.
(8) Al encontrar el tipo de referencia, asigna una memoria en la pila en el # 04 para aliasar la variable del parámetro
myParamList
.(9) Luego almacena el valor #FF también en él.
(10) Runtime crea un objeto de lista en el montón en la ubicación de memoria # 004 y reemplaza #FF en # 04 con este valor (o desreferencia el objeto de lista original y señala el nuevo objeto de lista en este método)
La dirección en # 00 no se altera y conserva la referencia a #FF (o el
myList
puntero original no se altera).La palabra clave ref es una directiva del compilador para omitir la generación de código de tiempo de ejecución para (8) y (9), lo que significa que no habrá asignación de montón para los parámetros del método. Utilizará el puntero original # 00 para operar el objeto en #FF. Si el puntero original no se inicializa, el tiempo de ejecución dejará de quejarse de que no puede continuar ya que la variable no está inicializada
La palabra clave out es una directiva del compilador que es casi lo mismo que ref con una ligera modificación en (9) y (10). El compilador espera que el argumento no se inicialice y continuará con (8), (4) y (5) para crear un objeto en el montón y almacenar su dirección inicial en la variable del argumento. No se generará ningún error no inicializado y se perderá cualquier referencia previa almacenada.
fuente
Además de permitirle reasignar la variable de otra persona a una instancia diferente de una clase, devolver múltiples valores, etc., usando
ref
o leout
permite a otra persona saber qué necesita de ellos y qué piensa hacer con la variable que proporcionanNo es necesario
ref
oout
si todo lo que vas a hacer es modificar las cosas dentro de laMyClass
instancia que se pasa en el argumentosomeClass
.someClass.Message = "Hello World"
si se utilizaref
,out
o nadasomeClass = new MyClass()
dentromyFunction(someClass)
intercambia el objeto visto por elsomeClass
en el alcance delmyFunction
método solamente. El método de llamada aún conoce laMyClass
instancia original que creó y pasó a su métodoUsted necesita
ref
oout
si usted planea en el bombeo desomeClass
salida para un objeto completamente nuevo y desea que el método de llamada para ver su cambiosomeClass = new MyClass()
dentromyFunction(out someClass)
cambia el objeto visto por el método que llamómyFunction
Existen otros programadores
Y quieren saber qué vas a hacer con sus datos. Imagine que está escribiendo una biblioteca que será utilizada por millones de desarrolladores. Desea que sepan qué va a hacer con sus variables cuando llaman a sus métodos
El uso
ref
hace una declaración de "Pase una variable asignada a algún valor cuando llame a mi método. Tenga en cuenta que podría cambiarlo por algo completamente diferente durante el curso de mi método. No espere que su variable apunte al objeto antiguo Cuando termine"El uso
out
hace una declaración de "Pasar una variable de marcador de posición a mi método. No importa si tiene un valor o no; el compilador me obligará a asignarlo a un nuevo valor. Garantizo absolutamente que el objeto señalado por su variable antes de llamar a mi método, será diferente para cuando terminePor cierto, en C # 7.2 también hay un
in
modificadorY eso evita que el método cambie la instancia pasada por una instancia diferente. Piense en ello como decir a esos millones de desarrolladores "pásenme su referencia variable original, y prometo no cambiar sus datos cuidadosamente elaborados por otra cosa".
in
tiene algunas peculiaridades, y en algunos casos, como cuando se puede requerir una conversión implícita para hacer que su corto sea compatible con unin int
compilador, temporalmente hará un int, lo ampliará, lo pasará por referencia y terminará. Puede hacer esto porque has declarado que no te meterás con eso.Microsoft hizo esto con los
.TryParse
métodos en los tipos numéricos:Al marcar el parámetro como
out
están declarando activamente aquí " definitivamente vamos a cambiar su minuciosamente elaborado valor de 98234957 por algo más"Por supuesto, tienen que hacerlo, por ejemplo, para analizar tipos de valores porque si no se permitiera al método de análisis intercambiar el tipo de valor por otra cosa, no funcionaría muy bien ... Pero imagine que hubiera algún método ficticio en algunos biblioteca que estás creando:
Puedes ver que es un
out
, y así puedes saber que si pasas horas calculando números, creando el SomeClass perfecto:Bueno, eso fue una pérdida de tiempo, tomar todas esas horas para hacer esa clase perfecta. Definitivamente será desechado y reemplazado por PoorlyNamedMethod
fuente
Para aquellos que buscan una respuesta concisa.
fuente
Para ilustrar las muchas explicaciones excelentes, desarrollé la siguiente aplicación de consola:
AppendWorld
: Se pasa una copia deStringList
namedLiStri
. Al comienzo del método, esta copia hace referencia a la lista original y, por lo tanto, puede usarse para modificar esta lista. Más tarde haceLiStri
referencia a otroList<string>
objeto dentro del método que no afecta a la lista original.HalloWelt
:LiStriRef
es un alias de lo ya inicializadoListStringRef
. ElList<string>
objeto pasado se usa para inicializar uno nuevo, porref
lo tanto, era necesario.CiaoMondo
:LiStriOut
es un alias deListStringOut
y debe inicializarse.Entonces, si un método simplemente modifica el objeto al que hace referencia la variable pasada, el compilador no le permitirá usarlo
out
y no debe usarloref
porque confundiría no al compilador sino al lector del código. Si el método hará que el argumento pasado haga referencia a otro objeto, úseloref
para un objeto ya inicializado yout
para métodos que deben inicializar un nuevo objeto para el argumento pasado. Además de eso,ref
yout
comportarse igual.fuente
Son más o menos lo mismo: la única diferencia es que una variable que pasa como parámetro de salida no necesita inicializarse, y el método que usa el parámetro ref tiene que establecerla en algo.
Los parámetros de referencia son para datos que pueden modificarse, los parámetros de salida son para datos que son una salida adicional para la función (por ejemplo, int. TryParse) que ya están utilizando el valor de retorno para algo.
fuente
A continuación, he mostrado un ejemplo usando tanto Ref como out . Ahora, todos estarán libres de ref y out.
En el ejemplo mencionado a continuación, cuando comento // myRefObj = new myClass {Name = "ref outside called !!"}; línea, se obtiene un error diciendo "El uso de variable local no asignada 'myRefObj'" , pero no hay tal error en la salida .
Dónde usar Ref : cuando llamamos a un procedimiento con un parámetro in y se usará el mismo parámetro para almacenar la salida de ese proceso.
Dónde usar Out: cuando llamamos a un procedimiento sin parámetro in y el mismo parámetro se utilizará para devolver el valor de ese proceso. También tenga en cuenta la salida
fuente
puede verificar este código, le describirá su diferencia completa cuando use "ref", lo que significa que ya ha inicializado ese int / string
pero cuando usa "out" funciona en ambas condiciones, ya sea que inicialice ese int / string o no, pero debe inicializar ese int / string en esa función
fuente
Ref: la palabra clave ref se usa para pasar un argumento como referencia. Esto significa que cuando el valor de ese parámetro se cambia en el método, se refleja en el método de llamada. Un argumento que se pasa usando una palabra clave ref debe inicializarse en el método de llamada antes de pasarlo al método llamado.
Out: la palabra clave out también se usa para pasar un argumento como la palabra clave ref, pero el argumento se puede pasar sin asignarle ningún valor. Un argumento que se pasa usando una palabra clave out debe inicializarse en el método llamado antes de que regrese al método de llamada.
Ref y fuera en método de sobrecarga
Tanto ref como out no se pueden usar en la sobrecarga de métodos simultáneamente. Sin embargo, ref y out se tratan de manera diferente en tiempo de ejecución, pero se tratan igual en tiempo de compilación (CLR no diferencia entre los dos mientras crea IL para ref y out).
fuente
Desde el punto de vista de un método que recibe un parámetro, la diferencia entre
ref
yout
es que C # requiere que los métodos deben escribir en cadaout
parámetro antes de regresar, y no deben hacer nada con dicho parámetro, aparte de pasarlo comoout
parámetro o escribir en él , hasta que se haya pasado comoout
parámetro a otro método o se haya escrito directamente. Tenga en cuenta que algunos otros idiomas no imponen tales requisitos; un método virtual o de interfaz que se declara en C # con unout
parámetro puede anularse en otro idioma que no imponga restricciones especiales sobre dichos parámetros.Desde el punto de vista de la persona que llama, C # en muchas circunstancias asumirá que llamar a un método con un
out
parámetro hará que la variable pasada se escriba sin haber sido leída primero. Esta suposición puede no ser correcta al llamar a métodos escritos en otros idiomas. Por ejemplo:Si
myDictionary
identifica unaIDictionary<TKey,TValue>
implementación escrita en un lenguaje que no sea C #, aunqueMyStruct s = new MyStruct(myDictionary);
parezca una tarea, podría dejars
sin modificar.Tenga en cuenta que los constructores escritos en VB.NET, a diferencia de los de C #, no hacen suposiciones sobre si los métodos llamados modificarán algún
out
parámetro y borrarán todos los campos incondicionalmente. El comportamiento extraño aludido anteriormente no ocurrirá con el código escrito completamente en VB o completamente en C #, pero puede ocurrir cuando el código escrito en C # llama a un método escrito en VB.NET.fuente
Si desea pasar su parámetro como una referencia, debe inicializarlo antes de pasar el parámetro a la función; de lo contrario, el compilador mismo mostrará el error. Pero en caso de que no haya un parámetro, no necesita inicializar el parámetro del objeto antes de pasarlo al método. Puede inicializar el objeto en el propio método de llamada.
fuente
Tenga en cuenta que el parámetro de referencia que se pasa dentro de la función se trabaja directamente.
Por ejemplo,
Esto escribirá Perro, no Gato. Por lo tanto, debe trabajar directamente en someObject.
fuente
Puede que no sea tan bueno en esto, pero seguramente las cadenas (aunque técnicamente son tipos de referencia y viven en el montón) se pasan por valor, no por referencia.
Es por eso que necesita ref si desea que existan cambios fuera del alcance de la función que los hace, de lo contrario no está pasando una referencia.
Por lo que sé, solo necesita ref para estructuras / tipos de valor y la cadena en sí, ya que la cadena es un tipo de referencia que finge que es pero no es un tipo de valor.
Sin embargo, podría estar completamente equivocado aquí, soy nuevo.
fuente
Capitalize()
que cambiaría el contenido de la cadena a mayúsculas. Si luego reemplazó su líneaa = "testing";
cona.Capitalize();
, entonces su salida sería "HOLA", no "Hola". Una de las ventajas de los tipos inmutables es que puede pasar referencias y no preocuparse de que otro código cambie el valor.