Domanda

Diciamo che vuoi produrre o concatenare stringhe.Quale dei seguenti stili preferisci?

  • var p = new { FirstName = "Bill", LastName = "Gates" };

  • Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

  • Console.WriteLine(p.FirstName + " " + p.LastName);

Preferisci usare il formato o semplicemente concatenare le stringhe?Qual è il tuo preferito?Uno di questi ti fa male agli occhi?

Hai qualche argomento razionale per usare l'uno e non l'altro?

Io opterei per la seconda.

È stato utile?

Soluzione

Prova questo codice.

È una versione leggermente modificata del tuo codice.
1.Ho rimosso Console.WriteLine perché probabilmente è più lento di qualche ordine di grandezza rispetto a quello che sto cercando di misurare.
2.Faccio partire il cronometro prima del loop e lo fermo subito dopo, in questo modo non perdo precisione se la funzione impiega ad esempio 26,4 tick per essere eseguita.
3.Il modo in cui hai diviso il risultato per alcune iterazioni era sbagliato.Guarda cosa succede se hai 1000 millisecondi e 100 millisecondi.In entrambe le situazioni, otterrai 0 ms dopo averlo diviso per 1000000.

Stopwatch s = new Stopwatch();

var p = new { FirstName = "Bill", LastName = "Gates" };

int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;

string result;
s.Start();
for (var i = 0; i < n; i++)
    result = (p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
    result = string.Format("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();


Console.Clear();
Console.WriteLine(n.ToString()+" x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Thread.Sleep(4000);

Questi sono i miei risultati:

1000000 x risultato = string.Format("{0} {1}", p.Nome, p.Cognome);preso:618ms - 2213706 tick
1000000 x risultato = (p.Nome + " " + p.Cognome);preso:166ms - 595610 tick

Altri suggerimenti

Sono stupito che così tante persone vogliano trovare immediatamente il codice che viene eseguito più velocemente. Se UN MILIONE di iterazioni richiedono ANCORA meno di un secondo per essere elaborate, questo sarà in QUALSIASI MODO evidente all'utente finale?Non molto probabile.

Ottimizzazione prematura = FAIL.

Io andrei con il String.Format opzione, solo perché ha più senso dal punto di vista architettonico.Non mi interessa la performance finché non diventa un problema (e se così fosse, mi chiederei:Devo concatenare un milione di nomi contemporaneamente?Sicuramente non entreranno tutti sullo schermo...)

Valuta se il tuo cliente desidera successivamente modificarlo in modo da poter configurare se visualizzarlo "Firstname Lastname" O "Lastname, Firstname." Con l'opzione Formato, questo è semplice: basta sostituire la stringa di formato.Con il concat avrai bisogno di codice aggiuntivo.Certo, non sembra un grosso problema in questo particolare esempio, ma estrapolalo.

Oh caro, dopo aver letto una delle altre risposte ho provato a invertire l'ordine delle operazioni, quindi eseguendo prima la concatenazione, poi String.Format...

Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 8ms - 30488 ticks
Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 0ms - 182 ticks

Quindi l'ordine delle operazioni fa una ENORME differenza, anzi la primissima operazione è SEMPRE molto più lenta.

Ecco i risultati di un'esecuzione in cui le operazioni vengono completate più di una volta.Ho provato a cambiare gli ordini ma generalmente le cose seguono le stesse regole, una volta ignorato il primo risultato:

Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 5ms - 20335 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 156 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 122 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 181 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 122 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 142 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 117 ticks

Come puoi vedere, le esecuzioni successive dello stesso metodo (ho rifattorizzato il codice in 3 metodi) sono sempre più veloci.Il più veloce sembra essere il metodo Console.WriteLine(String.Concat(...)), seguito dalla normale concatenazione e quindi dalle operazioni formattate.

Il ritardo iniziale nell'avvio è probabilmente l'inizializzazione di Console Stream, poiché l'inserimento di Console.Writeline("Start!") prima della prima operazione riporta tutti i tempi in linea.

Le stringhe sono immutabili, ciò significa che la stessa piccola porzione di memoria viene utilizzata più e più volte nel codice.L'aggiunta delle stesse due stringhe e la creazione ripetuta della stessa nuova stringa non influisce sulla memoria..Net è abbastanza intelligente da utilizzare lo stesso riferimento di memoria.Pertanto il tuo codice non verifica veramente la differenza tra i due metodi concat.

Prova questo per la taglia:

Stopwatch s = new Stopwatch();

int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0, sbElapsedMilliseconds = 0, sbElapsedTicks = 0;

Random random = new Random(DateTime.Now.Millisecond);

string result;
s.Start();
for (var i = 0; i < n; i++)
    result = (random.Next().ToString() + " " + random.Next().ToString());
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();

s.Start();
for (var i = 0; i < n; i++)
    result = string.Format("{0} {1}", random.Next().ToString(), random.Next().ToString());
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();

StringBuilder sb = new StringBuilder();
s.Start();
for(var i = 0; i < n; i++){
    sb.Clear();
    sb.Append(random.Next().ToString());
    sb.Append(" ");
    sb.Append(random.Next().ToString());
    result = sb.ToString();
}
s.Stop();
sbElapsedMilliseconds = s.ElapsedMilliseconds;
sbElapsedTicks = s.ElapsedTicks;
s.Reset();

Console.WriteLine(n.ToString() + " x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(\" \"); sb.Append(random.Next().ToString()); result = sb.ToString(); took: " + (sbElapsedMilliseconds) + "ms - " + (sbElapsedTicks) + " ticks");
Console.WriteLine("****************");
Console.WriteLine("Press Enter to Quit");
Console.ReadLine();

Output di esempio:

1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 513ms - 1499816 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 393ms - 1150148 ticks
1000000 x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(" "); sb.Append(random.Next().ToString()); result = sb.ToString(); took: 405ms - 1185816 ticks

Peccato per i poveri traduttori

Se tu Sapere la tua domanda rimarrà in inglese, quindi va bene, salva il ticchettio dell'orologio.Tuttavia, molte culture vedrebbero solitamente Lastname Firstname, ad esempio, negli indirizzi.

Quindi usa string.Format(), soprattutto se la tua domanda andrà ovunque in cui l'inglese non sia la prima lingua.

Ecco i miei risultati su 100.000 iterazioni:

Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took (avg): 0ms - 689 ticks
Console.WriteLine(p.FirstName + " " + p.LastName); took (avg): 0ms - 683 ticks

Ed ecco il codice del banco:

Stopwatch s = new Stopwatch();

var p = new { FirstName = "Bill", LastName = "Gates" };

//First print to remove the initial cost
Console.WriteLine(p.FirstName + " " + p.LastName);
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

int n = 100000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;

for (var i = 0; i < n; i++)
{
    s.Start();
    Console.WriteLine(p.FirstName + " " + p.LastName);
    s.Stop();
    cElapsedMilliseconds += s.ElapsedMilliseconds;
    cElapsedTicks += s.ElapsedTicks;
    s.Reset();
    s.Start();
    Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
    s.Stop();
    fElapsedMilliseconds += s.ElapsedMilliseconds;
    fElapsedTicks += s.ElapsedTicks;
    s.Reset();
}

Console.Clear();

Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took (avg): " + (fElapsedMilliseconds / n) + "ms - " + (fElapsedTicks / n) + " ticks");
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took (avg): " + (cElapsedMilliseconds / n) + "ms - " + (cElapsedTicks / n) + " ticks");

Quindi, non so quale risposta contrassegnare come risposta :)

Concatenare le stringhe va bene in uno scenario semplice come quello: è più complicato con qualcosa di più complicato di quello, anche LastName, FirstName.Con il formato puoi vedere, a colpo d'occhio, quale sarà la struttura finale della stringa durante la lettura del codice, con la concatenazione diventa quasi impossibile discernere immediatamente il risultato finale (se non con un esempio molto semplice come questo).

Ciò significa, a lungo termine, che quando torni per apportare una modifica al formato della stringa, avrai la possibilità di entrare e apportare alcune modifiche alla stringa del formato, oppure corrugare la fronte e iniziare a muoverti ovunque. tipi di funzioni di accesso alle proprietà mescolati con testo, che è più probabile che introducano problemi.

Se utilizzi .NET 3.5 puoi utilizzare un metodo di estensione come questo e ottieni una sintassi semplice e improvvisata come questa:

string str = "{0} {1} is my friend. {3}, {2} is my boss.".FormatWith(prop1,prop2,prop3,prop4);

Infine, man mano che la tua applicazione diventa più complessa, potresti decidere che per mantenere in modo sano le stringhe nella tua applicazione, desideri spostarle in un file di risorse da localizzare o semplicemente in un helper statico.Questo sarà MOLTO più facile da ottenere se hai utilizzato i formati in modo coerente e il tuo codice può essere semplicemente refactoring per utilizzare qualcosa come

string name = String.Format(ApplicationStrings.General.InformalUserNameFormat,this.FirstName,this.LastName);

Per una manipolazione molto semplice utilizzerei la concatenazione, ma una volta superati i 2 o 3 elementi il ​​formato diventa più appropriato IMO.

Un altro motivo per preferire String.Format è che le stringhe .NET sono immutabili e in questo modo si creano meno copie temporanee/intermedie.

Anche se capisco perfettamente la preferenza di stile e ho scelto la concatenazione per la mia prima risposta in parte in base alle mie preferenze, parte della mia decisione era basata sul pensiero che la concatenazione sarebbe stata più veloce.Quindi, per curiosità, l'ho provato e i risultati sono stati sconcertanti, soprattutto per una corda così piccola.

Utilizzando il seguente codice:

    System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();

    var p = new { FirstName = "Bill", LastName = "Gates" };

    s.Start();
    Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
    s.Stop();
    Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");

    s.Reset();
    s.Start();
    Console.WriteLine(p.FirstName + " " + p.LastName);
    s.Stop();

    Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");

Ho ottenuto i seguenti risultati:

Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 2ms - 7280 ticks
Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 0ms - 67 ticks

L'utilizzo del metodo di formattazione è oltre 100 volte più lento!!La concatenazione non è stata nemmeno registrata come 1 ms, motivo per cui ho emesso anche i segni di spunta del timer.

Per la concatenazione di stringhe di base, generalmente utilizzo il secondo stile: più facile da leggere e più semplice.Tuttavia, se sto eseguendo una combinazione di stringhe più complicata, di solito scelgo String.Format.

String.Format consente di risparmiare su molte virgolette e vantaggi...

Console.WriteLine("User {0} accessed {1} on {2}.", user.Name, fileName, timestamp);
vs
Console.WriteLine("User " + user.Name + " accessed " + fileName + " on " + timestamp + ".");

Sono stati salvati solo pochi caratteri, ma penso che, in questo esempio, il formato lo renda molto più pulito.

Un test migliore sarebbe guardare la tua memoria utilizzando Perfmon e i contatori di memoria CLR.La mia comprensione è che l'intera ragione per cui si desidera utilizzare String.Format invece di concatenare semplicemente le stringhe è che, poiché le stringhe sono immutabili, si sta caricando inutilmente il garbage collector con stringhe temporanee che devono essere recuperate nel passaggio successivo.

StringBuilder e String.Format, sebbene potenzialmente più lenti, sono più efficienti in termini di memoria.

Cosa c'è di così brutto nella concatenazione di stringhe?

In genere preferisco la prima, perché soprattutto quando le corde diventano lunghe può essere molto più facile da leggere.

L'altro vantaggio credo sia legato alle prestazioni, poiché quest'ultimo esegue effettivamente 2 istruzioni di creazione di stringhe prima di passare la stringa finale al metodo Console.Write.Credo che String.Format utilizzi uno StringBuilder dietro le quinte, quindi si evitano più concatenazioni.

Va notato, tuttavia, che se i parametri che stai passando a String.Format (e altri metodi simili come Console.Write) sono tipi di valore, verranno inseriti in un box prima di essere passati, il che può fornire i propri risultati in termini di prestazioni. Post del blog su questo qui.

Tra una settimana, il 19 agosto 2015, questa domanda compirà esattamente sette (7) anni.Ora esiste un modo migliore per farlo. Meglio in termini di manutenibilità in quanto non ho eseguito alcun test delle prestazioni rispetto alla semplice concatenazione di stringhe (ma ha importanza di questi tempi?qualche millisecondo di differenza?).Il nuovo modo di farlo con C#6.0:

var p = new { FirstName = "Bill", LastName = "Gates" };
var fullname = $"{p.FirstName} {p.LastName}";

Questa nuova funzionalità è Meglio, IMO, e anzi meglio nel nostro caso poiché abbiamo codici in cui costruiamo querystring i cui valori dipendono da alcuni fattori.Immagina una stringa di query in cui abbiamo 6 argomenti.Quindi invece di fare un, ad esempio:

var qs = string.Format("q1={0}&q2={1}&q3={2}&q4={3}&q5={4}&q6={5}", 
    someVar, anotherVarWithLongName, var3, var4, var5, var6)

in può essere scritto così ed è più facile da leggere:

var qs=$"q1={someVar}&q2={anotherVarWithLongName}&q3={var3}&q4={var4}&q5={var5}&q6={var6}";

A partire da C# 6.0 stringhe interpolate può essere usato per fare questo, il che semplifica ancora di più il formato.

var name = "Bill";
var surname = "Gates";
MessageBox.Show($"Welcome to the show, {name} {surname}!");

Un'espressione di stringa interpolata assomiglia a una stringa modello che contiene espressioni.Un'espressione di stringa interpolata crea una stringa sostituendo le espressioni contenute con le rappresentazioni ToString dei risultati delle espressioni.

Le stringhe interpolate hanno prestazioni simili a String.Format, ma una migliore leggibilità e una sintassi più breve, grazie al fatto che valori ed espressioni vengono inseriti in linea.

Si prega di fare riferimento anche a questo articolo su dotnetperls sull'interpolazione delle stringhe.

Se stai cercando un modo predefinito per formattare le tue stringhe, questo ha senso in termini di leggibilità e prestazioni (tranne se i microsecondi faranno la differenza nel tuo caso d'uso specifico).

  1. La formattazione è il modo ".NET" di farlo.Alcuni strumenti di refactoring (Refactor!per esempio) proporrà anche di rifattorizzare il codice in stile concat per utilizzare lo stile di formattazione.
  2. La formattazione è più semplice da ottimizzare per il compilatore (anche se il secondo verrà probabilmente sottoposto a refactoring per utilizzare il metodo 'Concat' che è veloce).
  3. La formattazione è solitamente più chiara da leggere (specialmente con la formattazione “fantastica”).
  4. La formattazione implica chiamate implicite a ".ToString" su tutte le variabili, il che è positivo per la leggibilità.
  5. Secondo "Effective C#", le implementazioni .NET 'WriteLine' e 'Format' sono incasinate, inseriscono automaticamente tutti i tipi di valore (il che è negativo)."Effective C#" consiglia di eseguire esplicitamente le chiamate '.ToString', cosa che secondo me è fasulla (vedi Il messaggio di Jeff)
  6. Al momento, i suggerimenti sul tipo di formattazione non vengono controllati dal compilatore, con conseguenti errori di runtime.Tuttavia, questo potrebbe essere modificato nelle versioni future.

Utilizzerei String.Format, ma avrei anche la stringa di formato nei file di risorse in modo che possa essere localizzata per altre lingue.L'uso di una semplice stringa concat non ti consente di farlo.Ovviamente se non avrai mai bisogno di localizzare quella stringa, questo non è un motivo per pensarci.Dipende davvero da cosa serve la corda.

Se verrà mostrato all'utente, utilizzerei String.Format in modo da poterlo localizzare se necessario - e FxCop farò il controllo ortografico per me, per ogni evenienza :)

Se contiene numeri o qualsiasi altro elemento non stringa (ad es.date), utilizzerei String.Format perché mi dà di più controllo sulla formattazione.

Se è per creare una query come SQL, lo userei Linq.

Se per concatenare stringhe all'interno di un ciclo, utilizzerei StringBuilder per evitare problemi di prestazioni.

Se è per qualche output che l'utente non vedrà e non influirà sulle prestazioni, utilizzerei String.Format perché ho comunque l'abitudine di usarlo e ci sono abituato :)

Se hai a che fare con qualcosa che deve essere facile da leggere (e questa è la maggior parte del codice), resterei con la versione con sovraccarico dell'operatore A MENO CHE:

  • Il codice deve essere eseguito milioni di volte
  • Stai facendo tonnellate di concat (più di 4 sono una tonnellata)
  • Il codice è mirato al Compact Framework

In almeno due di queste circostanze, utilizzerei invece StringBuilder.

Scelgo in base alla leggibilità.Preferisco l'opzione formato quando c'è del testo attorno alle variabili.In questo esempio:

Console.WriteLine("User {0} accessed {1} on {2}.", 
                   user.Name, fileName, timestamp);

ne capisci il significato anche senza nomi variabili, mentre il concat è ingombro di virgolette e segni + e mi confonde gli occhi:

Console.WriteLine("User " + user.Name + " accessed " + fileName + 
                  " on " + timestamp + ".");

(Ho preso in prestito l'esempio di Mike perché mi piace)

Se la stringa di formato non significa molto senza nomi di variabili, devo usare concat:

   Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

L'opzione formato mi fa leggere i nomi delle variabili e mapparli sui numeri corrispondenti.L'opzione concat non lo richiede.Sono ancora confuso dalle virgolette e dai segni +, ma l'alternativa è peggiore.Rubino?

   Console.WriteLine(p.FirstName + " " + p.LastName);

Per quanto riguarda le prestazioni, mi aspetto che l'opzione format sia più lenta del concat, poiché il formato richiede che lo sia la stringa analizzato.Non ricordo di aver dovuto ottimizzare questo tipo di istruzioni, ma se lo facessi, guarderei string metodi come Concat() E Join().

L'altro vantaggio di format è che la stringa di formato può essere inserita in un file di configurazione.Molto utile con i messaggi di errore e il testo dell'interfaccia utente.

Se intendi localizzare il risultato, String.Format è essenziale perché linguaggi naturali diversi potrebbero non avere i dati nello stesso ordine.

Penso che questo dipenda fortemente da quanto sia complesso l'output.Tendo a scegliere lo scenario che funziona meglio in quel momento.

Scegli lo strumento giusto in base al lavoro: D Qualunque sia il più pulito!

Anch'io preferisco la seconda, ma al momento non ho argomenti razionali per sostenere questa posizione.

Ben fatto!

Appena aggiunto

        s.Start();
        for (var i = 0; i < n; i++)
            result = string.Concat(p.FirstName, " ", p.LastName);
        s.Stop();
        ceElapsedMilliseconds = s.ElapsedMilliseconds;
        ceElapsedTicks = s.ElapsedTicks;
        s.Reset();

Ed è ancora più veloce (suppongo che string.Concat sia chiamato in entrambi gli esempi, ma il primo richiede una sorta di traduzione).

1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 249ms - 3571621 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 65ms - 944948 ticks
1000000 x result = string.Concat(p.FirstName, " ", p.LastName); took: 54ms - 780524 ticks

Poiché non penso che le risposte qui coprano tutto, vorrei fare una piccola aggiunta qui.

Console.WriteLine(string format, params object[] pars) chiamate string.Format.Il "+" implica la concatenazione di stringhe.Non penso che questo abbia sempre a che fare con lo stile;Tendo a mescolare i due stili a seconda del contesto in cui mi trovo.

Risposta breve

La decisione che stai affrontando ha a che fare con l'allocazione delle stringhe.Cercherò di renderlo semplice.

Di' di averlo fatto

string s = a + "foo" + b;

Se lo esegui, valuterà come segue:

string tmp1 = a;
string tmp2 = "foo" 
string tmp3 = concat(tmp1, tmp2);
string tmp4 = b;
string s = concat(tmp3, tmp4);

tmp qui non è realmente una variabile locale, ma è temporanea per JIT (viene inserita nello stack IL).Se inserisci una stringa nello stack (come ldstr in IL per valori letterali), inserisci un riferimento a un puntatore a stringa nello stack.

Nel momento in cui chiami concat questo riferimento diventa un problema, perché non è disponibile alcun riferimento a una stringa che contenga entrambe le stringhe.Ciò significa che .NET deve allocare un nuovo blocco di memoria e quindi riempirlo con le due stringhe.Il motivo per cui questo è un problema è perché l’allocazione è relativamente costosa.

Il che cambia la domanda in:Come puoi ridurre il numero di concat operazioni?

Quindi la risposta approssimativa è: string.Format per >1 concat, '+' funzionerà perfettamente per 1 concat.E se non ti interessa ottimizzare le micro-prestazioni, string.Format funzionerà perfettamente nel caso generale.

Una nota sulla Cultura

E poi c'è una cosa chiamata cultura...

string.Format ti consente di utilizzare CultureInfo nella tua formattazione.Un semplice operatore "+" utilizza la lingua corrente.

Questa è un'osservazione particolarmente importante se stai scrivendo formati di file e f.ex. double valori che "aggiungi" a una stringa.Su macchine diverse, potresti ritrovarti con stringhe diverse se non le usi string.Format con un esplicito CultureInfo.

F.es.Considera cosa succede se cambi a "." Per un ',' mentre scrivi il tuo file di valori per la virgola ...in olandese il separatore decimale è una virgola, quindi il tuo utente potrebbe ricevere una sorpresa "divertente".

Risposta più dettagliata

Se non conosci in anticipo la dimensione esatta della stringa, è meglio utilizzare una policy come questa per sovrallocare i buffer utilizzati.Lo spazio libero viene prima riempito, dopodiché vengono copiati i dati.

Crescere significa allocare un nuovo blocco di memoria e copiare i vecchi dati nel nuovo buffer.Il vecchio blocco di memoria può quindi essere rilasciato.A questo punto ottieni la conclusione:coltivare è un’operazione costosa.

Il modo più pratico per farlo è utilizzare una politica di sovraallocazione.La politica più comune è quella di sovrassegnare i buffer in potenze di 2.Naturalmente, devi farlo in modo un po' più intelligente di così (poiché non ha senso crescere da 1,2,4,8 se sai già che ti servono 128 caratteri) ma ottieni l'immagine.La polizza garantisce che non siano necessarie troppe delle costose operazioni che ho descritto sopra.

StringBuilder è una classe che fondamentalmente sovrassegna il buffer sottostante in potenze di due. string.Format usi StringBuilder sotto il cappuccio.

Ciò rende la tua decisione un compromesso di base tra sovrassegnare e aggiungere (-multiplo) (con/senzaculture) o semplicemente allocare e aggiungere.

Personalmente, il secondo poiché tutto ciò che stai utilizzando è nell'ordine diretto in cui verrà visualizzato.Mentre con il primo devi abbinare {0} e {1} con la var corretta, il che è facile da sbagliare.

Almeno non è così grave come lo sprintf C++ in cui se sbagli il tipo di variabile tutto esploderà.

Inoltre, poiché il secondo è tutto in linea e non deve eseguire alcuna ricerca e sostituzione di tutte le cose {0}, quest'ultimo dovrebbe essere più veloce...anche se non lo so per certo.

In realtà mi piace il primo perché quando ci sono molte variabili mescolate al testo mi sembra più facile da leggere.Inoltre, è più semplice gestire le virgolette quando si utilizza il formato string.Format().Qui è analisi decente della concatenazione di stringhe.

Ho sempre seguito il percorso string.Format().Essere in grado di memorizzare formati in variabili come nell'esempio di Nathan è un grande vantaggio.In alcuni casi posso aggiungere una variabile, ma una volta concatenata più di una variabile, effettuo il refactoring per utilizzare la formattazione.

Oh, e solo per completezza, quanto segue è qualche tick più veloce della normale concatenazione:

Console.WriteLine(String.Concat(p.FirstName," ",p.LastName));

Il primo (formato) mi sembra migliore.È più leggibile e non stai creando oggetti stringa temporanei aggiuntivi.

Ero curioso di sapere dove si trovava StringBuilder con questi test.Risultati qui sotto...

class Program {
   static void Main(string[] args) {

      var p = new { FirstName = "Bill", LastName = "Gates" };

      var tests = new[] {
         new { Name = "Concat", Action = new Action(delegate() { string x = p.FirstName + " " + p.LastName; }) },
         new { Name = "Format", Action = new Action(delegate() { string x = string.Format("{0} {1}", p.FirstName, p.LastName); }) },
         new { Name = "StringBuilder", Action = new Action(delegate() {
            StringBuilder sb = new StringBuilder();
            sb.Append(p.FirstName);
            sb.Append(" ");
            sb.Append(p.LastName);
            string x = sb.ToString();
         }) }
      };

      var Watch = new Stopwatch();
      foreach (var t in tests) {
         for (int i = 0; i < 5; i++) {
            Watch.Reset();
            long Elapsed = ElapsedTicks(t.Action, Watch, 10000);
            Console.WriteLine(string.Format("{0}: {1} ticks", t.Name, Elapsed.ToString()));
         }
      }
   }

   public static long ElapsedTicks(Action ActionDelg, Stopwatch Watch, int Iterations) {
      Watch.Start();
      for (int i = 0; i < Iterations; i++) {
         ActionDelg();
      }
      Watch.Stop();
      return Watch.ElapsedTicks / Iterations;
   }
}

Risultati:

Concat: 406 ticks
Concat: 356 ticks
Concat: 411 ticks
Concat: 299 ticks
Concat: 266 ticks
Format: 5269 ticks
Format: 954 ticks
Format: 1004 ticks
Format: 984 ticks
Format: 974 ticks
StringBuilder: 629 ticks
StringBuilder: 484 ticks
StringBuilder: 482 ticks
StringBuilder: 508 ticks
StringBuilder: 504 ticks

Secondo il materiale di preparazione MCSD, Microsoft suggerisce di utilizzare l'operatore + quando si ha a che fare con un numero molto ridotto di concatenazioni (probabilmente da 2 a 4).Non sono ancora sicuro del perché, ma è qualcosa da considerare.

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