Sé que "cadena" en C # es un tipo de referencia. Esto está en MSDN. Sin embargo, este código no funciona como debería entonces:
class Test
{
public static void Main()
{
string test = "before passing";
Console.WriteLine(test);
TestI(test);
Console.WriteLine(test);
}
public static void TestI(string test)
{
test = "after passing";
}
}
La salida debe ser "antes de pasar" "después de pasar" ya que paso la cadena como parámetro y es un tipo de referencia, la segunda declaración de salida debe reconocer que el texto cambió en el método TestI. Sin embargo, obtengo "antes de pasar" "antes de pasar" haciendo que parezca que se pasa por valor, no por ref. Entiendo que las cadenas son inmutables, pero no veo cómo eso explicaría lo que está sucediendo aquí. ¿Qué me estoy perdiendo? Gracias.

Respuestas:
La referencia a la cadena se pasa por valor. Hay una gran diferencia entre pasar una referencia por valor y pasar un objeto por referencia. Es lamentable que la palabra "referencia" se use en ambos casos.
Si se hace pasar la cadena de referencia por referencia, que funcionará como se espera:
Ahora debe distinguir entre hacer cambios en el objeto al que se refiere una referencia y hacer un cambio en una variable (como un parámetro) para permitir que se refiera a un objeto diferente. No podemos realizar cambios en una cadena porque las cadenas son inmutables, pero podemos demostrarlo con un
StringBuilder:Vea mi artículo sobre paso de parámetros para más detalles.
fuente
referencedeso como respuestaSi tenemos que responder la pregunta: String es un tipo de referencia y se comporta como una referencia. Pasamos un parámetro que contiene una referencia, no la cadena real. El problema está en la función:
El parámetro
testcontiene una referencia a la cadena pero es una copia. Tenemos dos variables apuntando a la cadena. Y debido a que cualquier operación con cadenas realmente crea un nuevo objeto, hacemos nuestra copia local para apuntar a la nueva cadena. Pero latestvariable original no cambia.Las soluciones sugeridas para poner
refen la declaración de función y en la invocación funcionan porque no pasaremos el valor de latestvariable sino que le pasaremos solo una referencia. Por lo tanto, cualquier cambio dentro de la función reflejará la variable original.Quiero repetir al final: la cadena es un tipo de referencia, pero como es inmutable, la línea
test = "after passing";realmente crea un nuevo objeto y nuestra copia de la variabletestse cambia para que apunte a la nueva cadena.fuente
Como han dicho otros, el
Stringtipo en .NET es inmutable y su referencia se pasa por valor.En el código original, tan pronto como se ejecute esta línea:
entonces
testya no se refiere al objeto original. Creamos un nuevoStringobjeto y lo asignamostestpara hacer referencia a ese objeto en el montón administrado.Siento que muchas personas se tropiezan aquí ya que no hay un constructor formal visible para recordarles. En este caso, está sucediendo detrás de escena ya que el
Stringtipo tiene soporte de idioma en cómo se construye.Por lo tanto, es por eso que el cambio
testno es visible fuera del alcance delTestI(string)método - hemos pasado la referencia por su valor y ahora que el valor ha cambiado! Pero si laStringreferencia se pasó por referencia, entonces cuando la referencia haya cambiado la veremos fuera del alcance delTestI(string)método.En este caso, se necesita la palabra clave ref o out . Creo que la
outpalabra clave podría ser un poco más adecuada para esta situación particular.fuente
outdebe asignarse dentro del método para que el código se compile.refNo tiene tal requisito. Además, losoutparámetros se inicializan fuera del método: el código de esta respuesta es un contraejemplo.outparámetros se pueden inicializar fuera del método, pero no es necesario. En este caso, queremos inicializar eloutparámetro para demostrar un punto sobre la naturaleza delstringtipo en .NET.En realidad, hubiera sido lo mismo para cualquier objeto, es decir, ser un tipo de referencia y pasar por referencia son 2 cosas diferentes en c #.
Esto funcionaría, pero eso se aplica independientemente del tipo:
También acerca de que la cadena es un tipo de referencia, también es especial. Está diseñado para ser inmutable, por lo que todos sus métodos no modificarán la instancia (devuelven una nueva). También tiene algunas cosas adicionales para el rendimiento.
fuente
Aquí hay una buena manera de pensar sobre la diferencia entre los tipos de valor, el paso por valor, los tipos de referencia y el paso por referencia:
Una variable es un contenedor.
Una variable de tipo de valor contiene una instancia. Una variable de tipo de referencia contiene un puntero a una instancia almacenada en otro lugar.
La modificación de una variable de tipo de valor muta la instancia que contiene. La modificación de una variable de tipo de referencia muta la instancia a la que apunta.
Las variables de tipo de referencia separadas pueden apuntar a la misma instancia. Por lo tanto, la misma instancia puede ser mutada a través de cualquier variable que la señale.
Un argumento pasado por valor es un nuevo contenedor con una nueva copia del contenido. Un argumento pasado por referencia es el contenedor original con su contenido original.
Cuando un argumento de tipo valor se pasa por valor: la reasignación del contenido del argumento no tiene ningún efecto fuera del alcance, porque el contenedor es único. Modificar el argumento no tiene ningún efecto fuera del alcance, porque la instancia es una copia independiente.
Cuando un argumento de tipo de referencia se pasa por valor: la reasignación del contenido del argumento no tiene ningún efecto fuera del alcance, porque el contenedor es único. La modificación del contenido del argumento afecta el alcance externo, porque el puntero copiado apunta a una instancia compartida.
Cuando se pasa un argumento por referencia: la reasignación del contenido del argumento afecta el alcance externo, porque el contenedor se comparte. La modificación del contenido del argumento afecta el alcance externo, porque el contenido se comparte.
En conclusión:
Una variable de cadena es una variable de tipo de referencia. Por lo tanto, contiene un puntero a una instancia almacenada en otro lugar. Cuando se pasa por valor, su puntero se copia, por lo que modificar un argumento de cadena debería afectar la instancia compartida. Sin embargo, una instancia de cadena no tiene propiedades mutables, por lo que un argumento de cadena no se puede modificar de todos modos. Cuando se pasa por referencia, el contenedor del puntero se comparte, por lo que la reasignación seguirá afectando el alcance externo.
fuente
" Una imagen vale más que mil palabras ".
Tengo un ejemplo simple aquí, es similar a su caso.
Esto es lo que pasó:
s1y lass2variables hacen referencia al mismo"abc"objeto de cadena."abc"objeto de cadena no se modifica a sí mismo (a"def"), sino"def"que se crea un nuevo objeto de cadena y luego haces1referencia a él.s2todavía se hace referencia al"abc"objeto de cadena, por lo que ese es el resultadofuente
Las respuestas anteriores son útiles, solo me gustaría agregar un ejemplo que creo que demuestra claramente lo que sucede cuando pasamos el parámetro sin la palabra clave ref, incluso cuando ese parámetro es un tipo de referencia:
fuente
Para mentes curiosas y para completar la conversación: Sí, String es un tipo de referencia :
¡Pero tenga en cuenta que este cambio solo funciona en un bloque inseguro ! porque las cadenas son inmutables (de MSDN):
Y tenga en cuenta que:
fuente
Creo que su código es análogo al siguiente, y no debería haber esperado que el valor haya cambiado por la misma razón que no lo haría aquí:
fuente
Tratar:
fuente