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
referenced
eso 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
test
contiene 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 latest
variable original no cambia.Las soluciones sugeridas para poner
ref
en la declaración de función y en la invocación funcionan porque no pasaremos el valor de latest
variable 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 variabletest
se cambia para que apunte a la nueva cadena.fuente
Como han dicho otros, el
String
tipo 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
test
ya no se refiere al objeto original. Creamos un nuevoString
objeto y lo asignamostest
para 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
String
tipo tiene soporte de idioma en cómo se construye.Por lo tanto, es por eso que el cambio
test
no 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 laString
referencia 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
out
palabra clave podría ser un poco más adecuada para esta situación particular.fuente
out
debe asignarse dentro del método para que el código se compile.ref
No tiene tal requisito. Además, losout
parámetros se inicializan fuera del método: el código de esta respuesta es un contraejemplo.out
parámetros se pueden inicializar fuera del método, pero no es necesario. En este caso, queremos inicializar elout
parámetro para demostrar un punto sobre la naturaleza delstring
tipo 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ó:
s1
y lass2
variables 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 haces1
referencia a él.s2
todaví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