Quando passo a string
a una funzione, viene passato un puntatore al contenuto della stringa o l'intera stringa viene passata alla funzione sullo stack come struct
sarebbe?
Quando passo a string
a una funzione, viene passato un puntatore al contenuto della stringa o l'intera stringa viene passata alla funzione sullo stack come struct
sarebbe?
Risposte:
Viene passato un riferimento; tuttavia, non è tecnicamente passato per riferimento. Questa è una distinzione sottile, ma molto importante. Considera il codice seguente:
void DoSomething(string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomething(strMain);
Console.WriteLine(strMain); // What gets printed?
}
Ci sono tre cose che devi sapere per capire cosa succede qui:
strMain
non vengono passate per riferimento. È un tipo di riferimento, ma il riferimento stesso viene passato per valore . Ogni volta che passi un parametro senza la ref
parola chiave (senza contare i out
parametri), hai passato qualcosa per valore.Quindi questo deve significare che stai ... passando un riferimento per valore. Poiché è un tipo di riferimento, solo il riferimento è stato copiato nello stack. Ma cosa significa?
Le variabili C # sono tipi di riferimento o tipi di valore . I parametri C # vengono passati per riferimento o passati per valore . La terminologia è un problema qui; questi suonano come la stessa cosa, ma non lo sono.
Se si passa un parametro di QUALSIASI tipo e non si utilizza la ref
parola chiave, l'hai passato per valore. Se l'hai passato per valore, quello che hai veramente passato era una copia. Ma se il parametro era un tipo di riferimento, la cosa che hai copiato era il riferimento, non qualunque cosa stesse puntando.
Ecco la prima riga del Main
metodo:
string strMain = "main";
Abbiamo creato due cose su questa riga: una stringa con il valore main
memorizzato da qualche parte in memoria e una variabile di riferimento chiamata che strMain
punta ad esso.
DoSomething(strMain);
Ora passiamo quel riferimento a DoSomething
. L'abbiamo passato per valore, quindi significa che ne abbiamo fatto una copia. È un tipo di riferimento, quindi significa che abbiamo copiato il riferimento, non la stringa stessa. Ora abbiamo due riferimenti che puntano ciascuno allo stesso valore in memoria.
Ecco la parte superiore del DoSomething
metodo:
void DoSomething(string strLocal)
Nessuna ref
parola chiave, quindi strLocal
e strMain
sono due riferimenti diversi che puntano allo stesso valore. Se riassegniamo strLocal
...
strLocal = "local";
... non abbiamo cambiato il valore memorizzato; abbiamo preso il riferimento chiamato strLocal
e lo abbiamo puntato su una stringa nuova di zecca. Cosa succede strMain
quando lo facciamo? Niente. Sta ancora indicando la vecchia corda.
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"
Cambiamo lo scenario per un secondo. Immagina di non lavorare con le stringhe, ma con qualche tipo di riferimento mutevole, come una classe che hai creato.
class MutableThing
{
public int ChangeMe { get; set; }
}
Se segui il riferimento objLocal
all'oggetto a cui punta, puoi modificarne le proprietà:
void DoSomething(MutableThing objLocal)
{
objLocal.ChangeMe = 0;
}
Ce n'è ancora solo uno MutableThing
in memoria e sia il riferimento copiato che il riferimento originale lo indicano ancora. Le proprietà dello MutableThing
stesso sono cambiate :
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, ma le stringhe sono immutabili! Non ci sono ChangeMe
proprietà da impostare. Non puoi fare strLocal[3] = 'H'
in C # come potresti fare con un char
array in stile C ; devi invece costruire una stringa completamente nuova. L'unico modo per cambiare strLocal
è puntare il riferimento su un'altra stringa, e questo significa che nulla di ciò che fai strLocal
può influire strMain
. Il valore è immutabile e il riferimento è una copia.
Per dimostrare che c'è una differenza, ecco cosa succede quando passi un riferimento per riferimento:
void DoSomethingByReference(ref string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomethingByReference(ref strMain);
Console.WriteLine(strMain); // Prints "local"
}
Questa volta, la stringa in viene Main
davvero modificata perché hai passato il riferimento senza copiarlo nello stack.
Quindi, anche se le stringhe sono tipi di riferimento, passarle per valore significa che qualsiasi cosa accada nel chiamato non influenzerà la stringa nel chiamante. Ma poiché sono tipi di riferimento, non è necessario copiare l'intera stringa in memoria quando si desidera passarla.
ref
parola chiave. Per dimostrare che passare per riferimento fa la differenza, guarda questa demo: rextester.com/WKBG5978
ref
parola chiave ha utilità, stavo solo cercando di spiegare perché si potrebbe pensare di passare un tipo di riferimento per valore in C # sembra la nozione "tradizionale" (cioè C) di passare per riferimento (e passare un tipo di riferimento per riferimento in C # sembra più come passare un riferimento a un riferimento in base al valore).
Foo(string bar)
potrebbe essere pensato come Foo(char* bar)
, mentre Foo(ref string bar)
sarebbe Foo(char** bar)
(o Foo(char*& bar)
o Foo(string& bar)
in C ++). Certo, non è come dovresti pensarci tutti i giorni, ma in realtà mi ha aiutato a capire finalmente cosa sta succedendo sotto il cofano.
Le stringhe in C # sono oggetti di riferimento immutabili. Ciò significa che i riferimenti ad essi vengono passati (per valore) e, una volta creata una stringa, non è possibile modificarla. I metodi che producono versioni modificate della stringa (sottostringhe, versioni tagliate, ecc.) Creano copie modificate della stringa originale.
Le stringhe sono casi speciali. Ogni istanza è immutabile. Quando si modifica il valore di una stringa si alloca una nuova stringa in memoria.
Quindi solo il riferimento viene passato alla tua funzione, ma quando la stringa viene modificata diventa una nuova istanza e non modifica la vecchia istanza.
Uri
(class) e Guid
(struct) sono casi speciali. Non vedo come si System.String
comporti come un "tipo di valore" più di altri tipi immutabili ... di origine di classe o struttura.
Uri
& Guid
- puoi semplicemente assegnare un valore letterale stringa a una variabile stringa. La stringa sembra essere modificabile, come un int
essere riassegnato, ma sta creando un oggetto implicitamente, nessuna new
parola chiave.