Domanda

In .NET, le stringhe sono immutabili e sono variabili di tipo riferimento. Questo spesso si presenta come una sorpresa per nuovi sviluppatori .NET che possono li scambiano per oggetti di tipo valore a causa del loro comportamento. Tuttavia, a parte la pratica di usare StringBuilder a lungo la concatenazione esp. in loop, c'è qualche ragione, in pratica, che bisogna conoscere questa distinzione?

Quali scenari del mondo reale sono aiutati o evitato attraverso la comprensione della distinzione valore di riferimento per quanto riguarda le stringhe .NET vs. solo facendo finta / malinteso loro di essere i tipi di valore?

È stato utile?

Soluzione

Il design del strings era deliberatamente in modo tale che non dovreste aver bisogno di preoccuparsi troppo su di esso come programmatore. In molte situazioni, questo significa che si può solo assegnare, spostare, copiare, modificare le stringhe senza pensare troppo delle possibili conseguenze intricati se un altro riferimento alla stringa esistito e sarebbe cambiato allo stesso tempo (come accade con riferimenti a oggetti).

parametri di stringa in una chiamata di metodo

(EDIT: questa sezione aggiunto in seguito)
Quando le stringhe sono passati a un metodo, vengono passati per riferimento. Quando sono letti solo nel corpo del metodo, niente di speciale accade. Ma quando vengono modificati, viene creata una copia e la variabile temporanea viene utilizzato nel resto del metodo. Questo processo è chiamato copy-on-write .

Quello che preoccupa juniores è che essi sono utilizzati per il fatto che gli oggetti sono riferimenti e sono cambiati in un metodo che cambia il parametro passato. Per fare lo stesso con le stringhe, hanno bisogno di utilizzare la parola chiave ref. Questo permette in realtà il riferimento della stringa da modificare e restituito alla funzione chiamante. Se non lo fai, la stringa non può essere modificata dal corpo del metodo:

void ChangeBad(string s)      { s = "hello world"; }
void ChangeGood(ref string s) { s = "hello world"; }

// in calling method:
string s1 = "hi";
ChangeBad(s1);       // s1 remains "hi" on return, this is often confusing
ChangeGood(ref s1);  // s1 changes to "hello world" on return

On StringBuilder

Questa distinzione è importante, ma i programmatori principianti di solito sono meglio non sapere troppo su di esso. Utilizzando StringBuilder quando si fa un sacco di stringa "edificio" è buono, ma spesso, l'applicazione avrà molto più pesce per friggere e il guadagno di poco le prestazioni di StringBuilder è trascurabile. Diffidare di programmatori che indicano che tutti manipolazione delle stringhe dovrebbe essere fatto utilizzando StringBuilder.

Come regola molto approssimativa: StringBuilder ha alcuni costi di creazione, ma l'accodamento è a buon mercato. String ha un costo di creazione a buon mercato, ma la concatenazione è relativamente costoso. Il punto di svolta è di circa 400-500 concatenazioni, a seconda delle dimensioni: dopo di che, StringBuilder diventa più efficiente

.

Ulteriori informazioni su StringBuilder vs prestazioni stringa

EDIT: sulla base di un commento da Konrad Rudolph, ho aggiunto questa sezione

.

Se la regola precedente del pollice ti fa chiedere, si consideri la seguente spiegazione un po 'più dettagliate:

  • StringBuilder con molte piccole stringhe aggiunge outruns concatenazione di stringhe piuttosto rapidamente (30, 50 accodamento), ma su 2μs, anche guadagno di prestazioni del 100% è spesso trascurabile (sicuro per alcune rare situazioni);
  • StringBuilder con alcuni grandi accodamento a corda (80 caratteri o stringhe più grandi) outruns concatenazione di stringhe solo dopo migliaia, a volte centesimi di migliaia di iterazioni e la differenza è spesso solo una piccola percentuale;
  • miscelazione azioni stringa (sostituire, inserire, sottostringa, espressioni regolari, ecc) spesso rende l'utilizzo StringBuilder o concatenazione di stringhe uguali;
  • String concatenazione delle costanti può essere ottimizzata distanza dal compilatore, CLR o JIT, non può per StringBuilder;
  • Codice spesso mescola + concatenazione, StringBuilder.Append, String.Format, ToString e altre operazioni sulle stringhe, utilizzando StringBuilder in questi casi non viene quasi mai efficace.

Così, quando è è efficace? Nei casi in cui vengono aggiunte molte piccole stringhe, vale a dire, per serializzare i dati in un file, per esempio, e quando non è necessario modificare i dati "scritto" una volta "scritto" per StringBuilder. E nei casi in cui molti metodi hanno bisogno di aggiungere qualcosa, perché StringBuilder è un tipo di riferimento e archi vengono copiati quando vengono modificati.

sulle stringhe internati

Un problema sorge - non solo con i programmatori minori - quando cercano di fare un confronto di riferimento e scoprire che a volte il risultato è vero, ed a volte è falso, in apparentemente le stesse situazioni. Quello che è successo? Quando le corde furono internati dal compilatore e aggiunti alla statica globale internati pool di stringhe, il confronto tradue stringhe possono puntare allo stesso indirizzo di memoria. Quando (riferimento!) Confrontare due stringhe uguali, uno internati e non, produrrà falso. Utilizzare confronto =, o Equals e non giocare con ReferenceEquals quando si tratta con le stringhe.

On String.Empty

Nella stessa lega adatta un comportamento strano che a volte si verifica quando utilizzare String.Empty: il String.Empty statico è sempre internato, ma una variabile con un valore assegnato non è. Tuttavia, per default il compilatore assegna String.Empty e scegliere lo stesso indirizzo di memoria. Risultato:. Una variabile stringa mutabile, se confrontato con ReferenceEquals, restituisce true, mentre ci si potrebbe aspettare falso invece

// emptiness is treated differently:
string empty1 = String.Empty;
string empty2 = "";
string nonEmpty1 = "something";
string nonEmpty2 = "something";

// yields false (debug) true (release)
bool compareNonEmpty = object.ReferenceEquals(nonEmpty1, nonEmpty2);

// yields true (debug) false (release, depends on .NET version and how it's assigned)
bool compareEmpty = object.ReferenceEquals(empty1, empty2);

In profondità

È fondamentalmente chiesto di quali situazioni possono verificarsi per i non iniziati. Credo che il mio punto si riduce a evitare object.ReferenceEquals perché non può essere attendibile se usato con le stringhe. La ragione è che internato stringa viene utilizzata quando la stringa è costante nel codice, ma non sempre. Non si può contare su questo comportamento. Anche se String.Empty e "" sono sempre internati, non è quando il compilatore ritiene che il valore è modificabile. Diverse opzioni di ottimizzazione (debug vs rilascio e altri) produrranno risultati diversi.

Quando do è necessario ReferenceEquals comunque? Con gli oggetti ha un senso, ma con le stringhe non è così. Insegnare a chiunque a lavorare con le stringhe per evitare il suo utilizzo a meno che non capiscono anche unsafe e appuntato oggetti.

Prestazioni

Quando le prestazioni è importante, si può scoprire che le stringhe sono in realtà non immutabile e che usando StringBuilder è non sempre il più veloce approccio .

Un sacco di informazioni che ho usato qui è in questo eccellente articolo sulle stringhe , insieme a un "come fare" per la manipolazione di stringhe in-place (stringhe mutevoli).

Aggiornamento: aggiunto il codice di esempio
Aggiornamento: aggiunto 'in profondita' la sezione (spero che qualcuno trovato utile;)
Aggiornamento: ha aggiunto alcuni link, sezione archi params
aggiunto Aggiornamento: la stima aggiunto per quando passare da stringhe a StringBuilder
Aggiornamento: ha aggiunto una sezione in più su StringBuilder vs prestazioni String, dopo un'osservazione di Konrad Rudolph

Altri suggerimenti

L'unica distinzione che conta davvero per la maggior parte del codice è il fatto che null può essere assegnato a variabili di stringa.

Una classe immutabile agisce come un tipo di valore in tutte le situazioni più comuni, e si può fare un bel po 'di programmazione senza preoccuparsi molto della differenza.

E 'quando si scava un po' più profondo e la cura di prestazioni che si dispone di reale utilità per la distinzione. Ad esempio, per sapere che, anche se passando una stringa come parametro a un metodo agisce come se viene creata una copia della stringa, la copia non effettivamente luogo. Questo potrebbe essere una sorpresa per chi è abituato alle lingue in cui le stringhe in realtà sono tipi di valore (come VB6?), E passando un sacco di stringhe come parametri non sarebbe un bene per le prestazioni.

String è una razza speciale. Sono tipo di riferimento ancora utilizzato dalla maggior parte dei codificatori come un tipo di valore. Rendendo immutabile e con il pool interno, ottimizza l'utilizzo della memoria che sarà enorme se si tratta di un tipo di valore puro.

Ulteriori letture qui:
C # .NET String oggetto è davvero per riferimento? su
SO
String.Intern Metodo su MSDN
stringa (Riferimenti per C #) su MSDN

Aggiornamento:
Si prega di fare riferimento al commento di abel a questo post. E 'corretto la mia dichiarazione fuorviante.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top