Alguien me preguntó el otro día cuándo deberían usar la palabra clave parámetro en outlugar de ref. Si bien (creo) entiendo la diferencia entre las palabras clave refy out(que se ha preguntado antes ) y la mejor explicación parece ser que ref== iny out, cuáles son algunos ejemplos (hipotéticos o de código) donde siempre debería usar outy no ref.
Como refes más general, ¿por qué quieres usarlo out? ¿Es solo azúcar sintáctico?

outno se puede leer antes de asignarla.refno tiene esta restricción Entonces ahí está eso.refes para entrada / salida, mientras queoutes un parámetro solo de salida.outvariables tienen que ser asignadas en la función.Respuestas:
Debe usar a
outmenos que lo necesiteref.Hace una gran diferencia cuando los datos necesitan ser ordenados, por ejemplo, a otro proceso, que puede ser costoso. Por lo tanto, debe evitar calcular el valor inicial cuando el método no lo utiliza.
Más allá de eso, también muestra al lector de la declaración o la llamada si el valor inicial es relevante (y potencialmente preservado), o desechado.
Como una diferencia menor, no es necesario inicializar un parámetro de salida.
Ejemplo para
out:donde GetBothNames es un método para recuperar dos valores atómicamente, el método no cambiará el comportamiento sean cuales sean ayb. Si la llamada va a un servidor en Hawai, copiar los valores iniciales de aquí a Hawai es una pérdida de ancho de banda. Un fragmento similar usando ref:
podría confundir a los lectores, porque parece que los valores iniciales de a y b son relevantes (aunque el nombre del método indicaría que no lo son).
Ejemplo para
ref:Aquí el valor inicial es relevante para el método.
fuente
reflos valores predeterminados.outparámetro, se requiere que el método de llamada asigne un valor antes de que el método regrese. - No tiene que hacer nada con un parámetro de referencia.Úselo para denotar que el parámetro no se está utilizando, solo se establece. Esto ayuda a la persona que llama a comprender que siempre está inicializando el parámetro.
Además, ref y out no son solo para tipos de valor. También le permiten restablecer el objeto al que hace referencia un tipo de referencia dentro de un método.
fuente
outlos parámetros se tratan como no asignados al ingresar a la función. No podrá inspeccionar su valor hasta que haya asignado definitivamente algún valor; no hay forma de usar el valor que tenía el parámetro cuando se llamó a la función.Tiene razón en que, semánticamente,
refproporciona la funcionalidad "dentro" y "fuera", mientras queoutsolo proporciona la funcionalidad "fuera". Hay algunas cosas a considerar:outrequiere que el método que acepta el parámetro DEBE, en algún momento antes de regresar, asignar un valor a la variable. Encontrará este patrón en algunas de las clases de almacenamiento de datos de clave / valorDictionary<K,V>, donde tiene funciones comoTryGetValue. Esta función toma unoutparámetro que contiene cuál será el valor si se recupera. No tendría sentido que la persona que llama transfiera un valor a esta función, por lo queoutse utiliza para garantizar que algún valor estará en la variable después de la llamada, incluso si no son datos "reales" (en el caso deTryGetValuedonde La clave no está presente).outy losrefparámetros se ordenan de manera diferente cuando se trata de código de interoperabilidadAdemás, como comentario aparte, es importante tener en cuenta que si bien los tipos de referencia y los tipos de valor difieren en la naturaleza de su valor, están disponibles en ambos lados, pero eso se debe a que la variable real es solo un puntero a otra ubicación de memoria; el contenido de la variable, la ubicación de la memoria, en realidad no cambió. cada variable en su aplicación apunta a una ubicación de memoria que contiene un valor , incluso para los tipos de referencia. Simplemente sucede que, con los tipos de referencia, el valor contenido en esa ubicación de memoria es otroubicación de memoria Cuando pasa valores a una función (o realiza cualquier otra asignación de variable), el valor de esa variable se copia en la otra variable. Para los tipos de valor, eso significa que se copia todo el contenido del tipo. Para los tipos de referencia, eso significa que se copia la ubicación de la memoria. De cualquier manera, crea una copia de los datos contenidos en la variable. La única relevancia real que tiene esto tiene que ver con la semántica de asignación; cuando se asigna una variable o se pasa por valor (el valor predeterminado), cuando se realiza una nueva asignación a la variable original (o nueva), no afecta a la otra variable. En el caso de los tipos de referencia, sí, los cambios realizados en la instancia
Al pasar con la
refpalabra clave se dice que tanto la variable original como el parámetro de la función en realidad apuntarán a la misma ubicación de memoria. Esto, nuevamente, afecta solo la semántica de asignación. Si se asigna un nuevo valor a una de las variables, como el otro apunta a la misma ubicación de memoria, el nuevo valor se reflejará en el otro lado.fuente
TryGetValueuso correctorefy nooutexplícito en el caso de no encontrar la clave.Depende del contexto de compilación (ver el ejemplo a continuación).
outyrefambos denotan el paso de variables por referencia, perorefrequieren que la variable se inicialice antes de pasar, lo que puede ser una diferencia importante en el contexto de Marshaling (Interop: UmanagedToManagedTransition o viceversa)MSDN advierte :
De los documentos oficiales de MSDN:
out:ref:Podemos verificar que la salida y la referencia son las mismas cuando se asigna el argumento:
Ejemplo de CIL :
Considere el siguiente ejemplo
en CIL, las instrucciones de
myfuncOutymyfuncRefson idénticas a las esperadas.nop : sin operación, ldloc : carga local, stloc : pila local, ldarg : argumento de carga, bs.s : rama a destino ...
(Ver: Lista de instrucciones CIL )
fuente
A continuación hay algunas notas que extraje de este artículo de codeproject en C # Out Vs Ref
Si eres una persona visual, mira este video de yourtube que demuestra la diferencia prácticamente https://www.youtube.com/watch?v=lYdcY5zulXA
La imagen de abajo muestra las diferencias más visualmente
fuente
one-way, lostwo-waytérminos pueden ser mal utilizados aquí. En realidad, ambos son bidireccionales, sin embargo, sus comportamientos conceptuales difieren en las referencias y valores de losDebe usarlo
refsi planea leer y escribir en el parámetro. Debe usarlooutsi solo planea escribir. En efecto,outes para cuando necesitaría más de un valor de retorno, o cuando no desea utilizar el mecanismo de retorno normal para la salida (pero esto debería ser raro).Existen mecanismos de lenguaje que ayudan a estos casos de uso.
Reflos parámetros deben haberse inicializado antes de pasar a un método (haciendo hincapié en el hecho de que son de lectura-escritura), y losoutparámetros no pueden leerse antes de que se les asigne un valor, y se garantiza que se han escrito al final de el método (enfatizando el hecho de que son solo de escritura). Contravenir estos principios da como resultado un error en tiempo de compilación.Por ejemplo,
int.TryParsedevuelveboolay acepta unout intparámetro:Este es un claro ejemplo de una situación en la que necesita generar dos valores: el resultado numérico y si la conversión fue exitosa o no. Los autores del CLR decidieron optar por
outaquí ya que no les importa lo queintpodría haber sido antes.Para
ref, puedes mirarInterlocked.Increment:Interlocked.Incrementincrementa atómicamente el valor dex. Como necesita leerxpara incrementarlo, esta es una situación en la querefes más apropiado. Te preocupas totalmente por lo quexera antes de que fuera pasadoIncrement.En la próxima versión de C #, incluso será posible declarar variables en los
outparámetros, agregando aún más énfasis en su naturaleza de solo salida:fuente
outparámetros no necesariamente se han inicializado, por lo que el compilador no le permitirá leer unoutparámetro hasta que haya escrito algo en él.nameOutsuifdeclaración porque no se le asignó nada antes.outes una versión más restrictiva deref.En el cuerpo de un método, debe asignar a todos los
outparámetros antes de abandonar el método. Tambiénoutse ignoran los valores asignados a un parámetro, mientras que serefrequiere que se asignen.Entonces
outte permite hacer:donde
refrequeriría que se asignen ayb.fuente
outes la versión menos restringida.reftiene "Condición previa: variable se asignaron claramente, posterior: variable se asignaron claramente", mientras queoutsólo ha `Condición posterior:. variable es definitivamente asignado" (Y como era de esperar, se requiere más de una implementación de la función con un menor número de condiciones previas)Como suena:
a cabo = Sólo initialize / llenar un parámetro (el parámetro debe estar vacío) devuélvalo a cabo llanura
ref = referencia, parámetros estándar (tal vez con el valor), pero la función puede modifiy ella.
fuente
Puede usar la
outpalabra clave contextual en dos contextos (cada uno es un enlace a información detallada), como un modificador de parámetros o en declaraciones de parámetros de tipo genérico en interfaces y delegados. Este tema trata sobre el modificador de parámetros, pero puede ver este otro tema para obtener información sobre las declaraciones de parámetros de tipo genérico.La
outpalabra clave hace que los argumentos se pasen por referencia. Esto es como larefpalabra clave, excepto querefrequiere que la variable se inicialice antes de pasarla. Para usar unoutparámetro, tanto la definición del método como el método de llamada deben usar explícitamente laoutpalabra clave. Por ejemplo: C #Aunque las variables pasaron como
outargumentos no tienen que inicializarse antes de pasarlas, se requiere que el método llamado asigne un valor antes de que el método regrese.Aunque las palabras clave
refyoutprovocan un comportamiento de tiempo de ejecución diferente, no se consideran parte de la firma del método en tiempo de compilación. Por lo tanto, los métodos no se pueden sobrecargar si la única diferencia es que un método toma unrefargumento y el otro toma unoutargumento. El siguiente código, por ejemplo, no se compilará: C #Sin embargo, se puede realizar una sobrecarga si un método toma un argumento
refoouty el otro no usa ninguno de estos, de la siguiente manera: C #Las propiedades no son variables y, por lo tanto, no se pueden pasar como
outparámetros.Para obtener información sobre cómo pasar matrices, consulte Pasar matrices usando
refyout(Guía de programación de C #).No puede usar las palabras clave
refyoutpara los siguientes tipos de métodos:Ejemplo
Declarar un
outmétodo es útil cuando desea que un método devuelva varios valores. El siguiente ejemplo se usaoutpara devolver tres variables con una sola llamada al método. Tenga en cuenta que el tercer argumento se asigna a nulo. Esto permite que los métodos devuelvan valores opcionalmente. C#fuente
¿Cómo usar
inooutorefen C #?C#tienen la misma funcionalidad pero con algunos límites .inLos argumentos no pueden ser modificados por el método llamado.refLos argumentos pueden ser modificados.refdebe inicializarse antes de ser utilizado por la persona que llama, puede leerse y actualizarse en el método.outlos argumentos deben ser modificados por la persona que llama.outlos argumentos deben inicializarse en el métodoinargumentos deben inicializarse antes de pasarlas en una llamada al método. Sin embargo, el método llamado puede no asignar un valor o modificar el argumento.No se pueden utilizar los
in,refyoutpalabras clave para los siguientes tipos de métodos:asyncmodificador.yield returnoyield break.fuente
Solo para aclarar en el comentario de OP que el uso en ref y out es una "referencia a un tipo de valor o estructura declarada fuera del método", que ya se ha establecido de forma incorrecta.
Considere el uso de ref en un StringBuilder, que es un tipo de referencia:
Según lo dispuesto a esto:
fuente
Un argumento pasado como ref debe inicializarse antes de pasar al método, mientras que el parámetro out no necesita inicializarse antes de pasar a un método.
fuente
¡Para que otros sepan que la variable se inicializará cuando regrese del método llamado!
Como se mencionó anteriormente: "para un parámetro de salida, el método de llamada debe asignar un valor antes de que el método regrese ".
ejemplo:
fuente
Básicamente ambos
refyoutpara pasar objeto / valor entre métodosLa palabra clave out hace que los argumentos se pasen por referencia. Esto es como la palabra clave ref, excepto que ref requiere que la variable se inicialice antes de pasarla.
out: El argumento no se inicializa y debe inicializarse en el métodoref: El argumento ya está inicializado y se puede leer y actualizar en el método.¿Cuál es el uso de "ref" para los tipos de referencia?
Puede cambiar la referencia dada a una instancia diferente.
¿Sabías?
Aunque las palabras clave ref y out provocan un comportamiento de tiempo de ejecución diferente, no se consideran parte de la firma del método en tiempo de compilación. Por lo tanto, los métodos no se pueden sobrecargar si la única diferencia es que un método toma un argumento ref y el otro saca un argumento out.
No puede usar las palabras clave ref y out para los siguientes tipos de métodos:
Las propiedades no son variables y, por lo tanto, no se pueden pasar como parámetros de salida.
fuente
Notas adicionales con respecto a C # 7:
en C # 7 no hay necesidad de predeclarar variables usando. Entonces un código como este:
Se puede escribir así:
Fuente: Novedades en C # 7.
fuente
Todavía siento la necesidad de un buen resumen, esto es lo que se me ocurrió.
Resumen,
Cuando estamos dentro de la función , así es como especificamos el control de acceso a datos variables ,
in= Rout= debe W antes de Rref= R + WExplicación,
inLa función solo puede LEER esa variable.
outLa variable no debe inicializarse primero porque, la
función DEBE ESCRIBIRLA antes de LEER .
refLa función puede LEER / ESCRIBIR a esa variable.
¿Por qué se llama así?
Centrándose en dónde se modifican los datos,
inLos datos solo deben establecerse antes de ingresar la función (in).
outLos datos solo deben establecerse antes de dejar la función (fuera).
refLos datos deben establecerse antes de ingresar la función (in).
Los datos se pueden configurar antes de dejar la función (fuera).
fuente
Cabe señalar que
ines una palabra clave válida a partir de C # ver 7.2 :fuente