Cuando paso una stringa una función, ¿se pasa un puntero al contenido de la cadena o se pasa toda la cadena a la función en la pila como lo structharía a?
fuente
Cuando paso una stringa una función, ¿se pasa un puntero al contenido de la cadena o se pasa toda la cadena a la función en la pila como lo structharía a?
Se pasa una referencia; sin embargo, técnicamente no se pasa por referencia. Ésta es una distinción sutil, pero muy importante. Considere el siguiente código:
void DoSomething(string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomething(strMain);
Console.WriteLine(strMain); // What gets printed?
}
Hay tres cosas que necesita saber para comprender lo que sucede aquí:
strMainno se pasa por referencia. Es un tipo de referencia, pero la referencia en sí se pasa por valor . Cada vez que pasa un parámetro sin la refpalabra clave (sin contar los outparámetros), pasa algo por valor.Eso debe significar que estás ... pasando una referencia por valor. Dado que es un tipo de referencia, solo se copió la referencia en la pila. Pero ¿qué significa eso?
Las variables de C # son tipos de referencia o tipos de valor . Los parámetros de C # se pasan por referencia o por valor . La terminología es un problema aquí; estos suenan como lo mismo, pero no lo son.
Si pasa un parámetro de CUALQUIER tipo y no usa la refpalabra clave, entonces lo ha pasado por valor. Si lo pasó por valor, lo que realmente aprobó fue una copia. Pero si el parámetro era un tipo de referencia, entonces lo que copió fue la referencia, no lo que apuntaba.
Aquí está la primera línea del Mainmétodo:
string strMain = "main";
Hemos creado dos cosas en esta línea: una cadena con el valor mainalmacenado en la memoria en algún lugar, y una variable de referencia llamada strMainapuntando hacia ella.
DoSomething(strMain);
Ahora pasamos esa referencia a DoSomething. Lo pasamos por valor, lo que significa que hicimos una copia. Es un tipo de referencia, lo que significa que copiamos la referencia, no la cadena en sí. Ahora tenemos dos referencias que cada una apunta al mismo valor en la memoria.
Aquí está la parte superior del DoSomethingmétodo:
void DoSomething(string strLocal)
Sin refpalabra clave, entonces strLocaly strMainson dos referencias diferentes que apuntan al mismo valor. Si reasignamos strLocal...
strLocal = "local";
... no hemos cambiado el valor almacenado; Tomamos la referencia llamada strLocaly la apuntamos a una cuerda nueva. ¿Qué pasa con strMaincuando hacemos eso? Nada. Todavía apunta a la vieja cuerda.
string strMain = "main"; // Store a string, create a reference to it
DoSomething(strMain); // Reference gets copied, copy gets re-pointed
Console.WriteLine(strMain); // The original string is still "main"
Cambiemos el escenario por un segundo. Imagina que no estamos trabajando con cadenas, sino con algún tipo de referencia mutable, como una clase que has creado.
class MutableThing
{
public int ChangeMe { get; set; }
}
Si sigue la referencia objLocalal objeto al que apunta, puede cambiar sus propiedades:
void DoSomething(MutableThing objLocal)
{
objLocal.ChangeMe = 0;
}
Todavía hay solo uno MutableThingen la memoria, y tanto la referencia copiada como la referencia original todavía apuntan a él. Las propiedades del MutableThingmismo han cambiado :
void Main()
{
var objMain = new MutableThing();
objMain.ChangeMe = 5;
Console.WriteLine(objMain.ChangeMe); // it's 5 on objMain
DoSomething(objMain); // now it's 0 on objLocal
Console.WriteLine(objMain.ChangeMe); // it's also 0 on objMain
}
¡Ah, pero las cuerdas son inmutables! No hay ninguna ChangeMepropiedad que configurar. No puede hacer strLocal[3] = 'H'en C # como podría hacerlo con una charmatriz de estilo C ; tienes que construir una cadena completamente nueva en su lugar. La única forma de cambiar strLocales apuntar la referencia a otra cadena, y eso significa que nada de lo que haga strLocalpuede afectar strMain. El valor es inmutable y la referencia es una copia.
Para demostrar que hay una diferencia, esto es lo que sucede cuando pasa una referencia por referencia:
void DoSomethingByReference(ref string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomethingByReference(ref strMain);
Console.WriteLine(strMain); // Prints "local"
}
Esta vez, la cadena en Mainrealmente se cambia porque pasó la referencia sin copiarla en la pila.
Entonces, aunque las cadenas son tipos de referencia, pasarlas por valor significa que lo que suceda en el destinatario de la llamada no afectará a la cadena en el llamador. Pero como son tipos de referencia, no es necesario que copie la cadena completa en la memoria cuando quiera pasarla.
refpalabra clave. Para demostrar que pasar por referencia marca la diferencia, vea esta demostración: rextester.com/WKBG5978refpalabra clave tiene utilidad, solo estaba tratando de explicar por qué uno podría pensar en pasar un tipo de referencia por valor en C # parece la noción "tradicional" (es decir, C) de pasar por referencia (y pasar un tipo de referencia por referencia en C # parece más como pasar una referencia a una referencia por valor).Foo(string bar)podría ser pensado comoFoo(char* bar)mientras queFoo(ref string bar)seríaFoo(char** bar)(oFoo(char*& bar), oFoo(string& bar)en C ++). Claro, no es como deberías pensarlo todos los días, pero en realidad me ayudó a comprender finalmente lo que está sucediendo bajo el capó.Las cadenas en C # son objetos de referencia inmutables. Esto significa que las referencias a ellos se pasan (por valor) y, una vez que se crea una cadena, no se puede modificar. Los métodos que producen versiones modificadas de la cadena (subcadenas, versiones recortadas, etc.) crean copias modificadas de la cadena original.
fuente
Las cadenas son casos especiales. Cada instancia es inmutable. Cuando cambia el valor de una cadena, está asignando una nueva cadena en la memoria.
Entonces, solo se pasa la referencia a su función, pero cuando se edita la cadena, se convierte en una nueva instancia y no modifica la instancia anterior.
fuente
Uri(clase) yGuid(estructura) también son casos especiales. No veo cómoSystem.Stringactúa como un "tipo de valor" más que otros tipos inmutables ... de orígenes de clase o estructura.Uri&Guid, solo puede asignar un valor literal de cadena a una variable de cadena. La cadena parece ser mutable, como unaintreasignación, pero está creando un objeto implícitamente, sinnewpalabra clave.