Output stringa: formato o concat in C #?


178

Supponiamo che tu voglia produrre o concaticare 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 concateni le stringhe? Qual è il tuo preferito? Uno di questi ti fa male agli occhi?

Hai degli argomenti razionali per usare l'uno e non l'altro?

Vorrei scegliere il secondo.

Risposte:


88

Prova questo codice.

È una versione leggermente modificata del tuo codice.
1. Ho rimosso Console.WriteLine in quanto probabilmente alcuni ordini di grandezza sono più lenti di quello che sto cercando di misurare.
2. Sto avviando il cronometro prima del ciclo e lo interrompo subito dopo, in questo modo non sto perdendo precisione se la funzione impiega ad esempio 26,4 tick per eseguire.
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 result = string.Format ("{0} {1}", p.FirstName, p.LastName); ha impiegato: 618 ms - 2213706 tick
1000000 x risultato = (p.FirstName + "" + p.LastName); preso: 166ms - 595610 tick


1
Molto interessante. Ho una media di 224ms contro 48ms, un miglioramento di x4,66, persino migliore del tuo x3,72. Mi chiedo se esiste uno strumento di post-compilazione in grado di riscrivere l'IL string.Formatche non utilizza alcuna funzionalità di formattazione composita (cioè semplicemente semplice {0}) e sostituirli con la concatenazione di stringhe notevolmente più veloce. Mi chiedo che una simile impresa sia realizzabile con un riscrittore di IL esistente come PostSharp.
Allon Guralnek,

31
Le stringhe sono immutabili, questo significa che lo stesso piccolo pezzo di memoria viene usato più volte nel tuo codice. L'aggiunta delle stesse due stringhe insieme e la creazione ripetuta della stessa nuova stringa non influiscono sulla memoria. .Net è abbastanza intelligente da usare lo stesso riferimento di memoria. Pertanto, il codice non verifica realmente la differenza tra i due metodi concat. Vedi il codice nella mia risposta di seguito.
Ludington,

1
Onestamente, concatengo sempre dal momento che è più facile da leggere per me e wow è più veloce :)
puretppc

Quindi la velocità è l'unica ragione per scegliere l'una rispetto all'altra?
Niico,

158

Sono sorpreso che così tante persone vogliano subito trovare il codice che esegue il più veloce. Se un milione di iterazioni ANCORA impiegano meno di un secondo per essere elaborate, questo sarà in QUALSIASI MODO evidente per l'utente finale? Non molto probabilmente

Ottimizzazione prematura = FAIL.

Vorrei scegliere l' String.Formatopzione, solo perché ha più senso dal punto di vista architettonico. Non mi interessa la performance fino a quando non diventa un problema (e se lo facesse, mi chiedo: devo concatenare un milione di nomi contemporaneamente? Sicuramente non si adatteranno tutti allo schermo ...)

Considera se in seguito il tuo cliente desidera modificarlo in modo da poter configurare se visualizzare "Firstname Lastname"o "Lastname, Firstname."Con l'opzione Formato, questo è facile: basta scambiare la stringa di formato. Con il concat, avrai bisogno di un codice extra. Certo che non sembra un grosso problema in questo esempio particolare ma estrapolare.


47
Un buon punto in termini di "Ottimizzazione precoce == FAIL", sì. Ma quando inizi a pagare per il footprint di esecuzione (cloud e infrastruttura come servizio, chiunque?) E / o inizi a supportare 1 milione di utenti su qualcosa, la risposta a un singolo utente su una richiesta non è la domanda. Il costo della gestione di una richiesta per un utente è un costo per la tua linea di fondo e un problema di scala se / quando arrivano altre migliaia di chiamate ...
Aidanapword,

23
Questo è totalmente sbagliato. In un ambiente di sviluppo Web, spesso il codice di generazione delle stringhe risulterà profondo sia nel modello che nelle visualizzazioni e nei controller e può essere chiamato decine di migliaia di volte per caricamento della pagina. Ridurre il tempo impiegato per valutare il codice di generazione di stringhe del 50% potrebbe essere una vittoria enorme.
Benjamin Sussman,

2
Una domanda come questa non si applicherà solo nella prima istanza del PO. La risposta è il tipo di cose che le persone possono ricordare come "in che modo devo assemblare le stringhe?" mentre scrivono tutto il loro codice.
Phil Miller,

6
@Benjamin: ... nel qual caso, potresti creare un profilo e trovare quello come il tuo collo di bottiglia. Scommetterei denaro che tu lo stia tirando dal nulla, comunque; avendo scritto e profilato un certo numero di webapp in passato, ho quasi sempre trovato il collo di bottiglia nei tempi di risposta (sul lato server) per essere le query del database.
BlueRaja - Danny Pflughoeft

2
Questa sicuramente NON è un'ottimizzazione prematura. Abbastanza l'errore. Le prestazioni delle stringhe possono bloccare completamente le interfacce utente, specialmente in .NET se stai eseguendo molta formattazione e creazione di stringhe. ubiquity.acm.org/article.cfm?id=1513451
user99999991

54

Oh caro - dopo aver letto una delle altre risposte ho provato a invertire l'ordine delle operazioni - quindi eseguendo prima la concatenazione, poi lo 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 GRANDE differenza, o meglio la prima operazione è SEMPRE molto più lenta.

Ecco i risultati di una corsa in cui le operazioni vengono completate più di una volta. Ho provato a cambiare gli ordini ma le cose generalmente 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 successive esecuzioni dello stesso metodo (ho riformattato il codice in 3 metodi) sono progressivamente 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 all'avvio è probabilmente l'inizializzazione di Console Stream, come il posizionamento di una Console.Writeline ("Start!") Prima che la prima operazione porti tutti i tempi in linea.


2
Quindi rimuovere Console.WriteLine completamente dai test. Sta distorcendo i risultati!
CShark,


36

Le stringhe sono immutabili, questo significa che lo stesso piccolo pezzo di memoria viene usato più volte nel tuo codice. L'aggiunta delle stesse due stringhe insieme e la creazione ripetuta della stessa nuova stringa non influiscono sulla memoria. .Net è abbastanza intelligente da usare lo stesso riferimento di memoria. Pertanto il tuo codice non verifica veramente la differenza tra i due metodi concat.

Prova questo per dimensioni:

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();

Uscita campione:

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

1
Aggiunto un StringBuilder e output di esempio alla risposta
mikeschuld

Vedo come string.Formatvale la pena usare il piccolo successo prestazionale qui. Architettonicamente è meglio in quanto significa che è possibile modificare il formato più facilmente. Ma il costruttore di corde non ne vedo davvero il punto. Ogni altro thread qui dice che dovresti usare Stringbuilder invece di concatenare stringhe. Qual è il vantaggio? Chiaramente non accelerare, come dimostra questo benchmark.
roryok,

22

Peccato che i poveri traduttori

Se sai che la tua applicazione rimarrà in inglese, allora va bene, salva i segni di spunta dell'orologio. Tuttavia, molte culture di solito vedono Cognome Nome, ad esempio, negli indirizzi.

Quindi usa string.Format(), specialmente se hai intenzione di portare la tua applicazione ovunque che l'inglese non sia la prima lingua.


2
Come si string.Format()comporterebbe diversamente nelle diverse culture? Non stampa ancora il nome e poi il cognome? Sembra che dovresti tener conto della diversa cultura in entrambe le situazioni. Sento che mi manca qualcosa qui.
Broots Waymb

2
Sono d'accordo con @DangerZone .. come string.Format()sapresti che stavi usando un nome per un indirizzo? Se string.Format()scambiato in {0} {1}base alla cultura, lo considererei rotto.
Alex McMillan,

2
Credo che il punto che Jeremy stava cercando di sottolineare sia che nello scenario descritto per supportare diversi paesi potrebbe essere opportuno estrarre la stringa di formato stessa in una risorsa linguistica. Per la maggior parte dei paesi tale stringa sarebbe "{0} {1}", ma per i paesi in cui il cognome è l'operazione tipica (ad esempio Ungheria, Hong Kong, Cambogia, Cina, Giappone, Corea, Madagascar, Taiwan, Vietnam e parti dell'India) quella stringa sarebbe invece "{1} {0}".
Richard J Foster,

Infatti. O, più sottilmente, aggiungi la stringa di formato come attributo della persona. Ad esempio, mi piace avere il mio cognome dopo il mio nome, ma il mio collega Beng no.
Jeremy McGee,

14

Ecco i miei risultati su oltre 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 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 di chi sia la risposta da contrassegnare come risposta :)


Perché lo sfondo è blu per questa risposta?
user88637

@yossi è blu perché il risponditore è lo stesso del richiedente
Davy8

9

Concatenare le stringhe va bene in uno scenario semplice come quello - è più complicato con qualcosa di più complicato di così, 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 (tranne che con un esempio molto semplice come questo).

Ciò che a lungo termine significa che quando torni a modificare il formato della stringa, avrai la possibilità di entrare e apportare alcune modifiche alla stringa del formato, o raggrinzire la fronte e iniziare a muoverti in tutto tipi di accessori di proprietà mescolati con testo, che è più probabile che introducano problemi.

Se stai usando .NET 3.5 puoi usare un metodo di estensione come questo e ottenere un flusso semplice, fuori dalla sintassi del bracciale in questo modo:

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

Infine, man mano che la tua applicazione cresce in complessità, puoi decidere che per mantenere in sicurezza le stringhe nella tua applicazione, devi spostarle in un file di risorse per localizzarle o semplicemente in un supporto statico. Questo sarà MOLTO più facile da ottenere se hai utilizzato costantemente i formati e il tuo codice può essere semplicemente riformulato per usare qualcosa come

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

7

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

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


6

Mentre 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 si basava sul pensiero che la concatenazione sarebbe stata più veloce. Quindi, per curiosità, l'ho provato e i risultati sono stati sbalorditivi, soprattutto per una stringa 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'uso del metodo di formattazione è oltre 100 volte più lento !! La concatenazione non si è nemmeno registrata come 1 ms, motivo per cui ho anche emesso i tick del timer.


2
Ma ovviamente dovresti eseguire l'operazione più di una volta per ottenere le misurazioni.
erikkallen,

2
E perdere la chiamata a Console.Writeline () poiché va oltre lo scopo della domanda?
Aidanapword il

hai provato con un costruttore di corde? ;)
niico,

6

A partire da C # 6.0 è possibile utilizzare stringhe interpolate per semplificare ulteriormente il formato.

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

Un'espressione di stringa interpolata si presenta come una stringa di 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 migliorano la leggibilità e la sintassi più breve, a causa del fatto che valori ed espressioni sono inseriti in linea.

Si prega di fare riferimento anche a questo articolo 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).


5

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 salva su molte citazioni 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.


5

Un test migliore sarebbe quello di guardare la tua memoria usando i contatori di memoria Perfmon e CLR. La mia comprensione è che l'intero motivo per cui si desidera utilizzare String.Format invece di concatenare 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 male nella concatenazione di stringhe?


Sono d'accordo; ogni operazione di stringa crea una nuova copia della stringa. Tutta quella memoria verrà recuperata dal garbage collector prima o poi. Quindi, allocare molte stringhe potrebbe tornare a morderti più tardi.
Marnix van Valen,

5

In genere preferisco il primo, poiché specialmente quando le stringhe si allungano può essere molto più facile da leggere.

L'altro vantaggio è che credo sia uno di prestazioni, poiché quest'ultimo esegue effettivamente 2 istruzioni per la creazione di stringhe prima di passare la stringa finale al metodo Console.Write. String.Format utilizza StringBuilder sotto le copertine, credo, quindi si evitano più concatenazioni.

Va notato, tuttavia, che se i parametri che si stanno passando a String.Format (e altri metodi simili come Console.Write) sono tipi di valore, questi verranno inscatolati prima di essere passati, il che può fornire le proprie prestazioni. Post di blog su questo qui .


1
Il post del blog è ora disponibile all'indirizzo: jeffbarnes.net/blog/post/2006/08/08/… . Soffro di insufficiente rappresentante da modificare.
Richard Slater,

5

Tra una settimana dal 19 agosto 2015, questa domanda avrà esattamente sette (7) anni. Ora c'è un modo migliore per farlo. Meglio in termini di manutenibilità in quanto non ho fatto alcun test delle prestazioni rispetto al semplice concatenamento di stringhe (ma importa in questi giorni? 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à è migliore , IMO, e in realtà migliore nel nostro caso in quanto abbiamo codici in cui costruiamo stringhe di query i cui valori dipendono da alcuni fattori. Immagina una querystring 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 in questo modo ed è più facile da leggere:

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

In effetti, il nuovo modo di C # 6.0 è migliore rispetto alle alternative precedenti, almeno dal punto di vista della leggibilità.
Philippe,

Giusto. Ed è anche più sicuro in quanto non devi preoccuparti di quale oggetto va a quale indice (segnaposto) poiché metterai direttamente gli oggetti dove vuoi che sia.
von v.

A proposito, in realtà chiama Format (almeno con Roslyn).
Philippe,

A proposito, a cosa si riferisce questo poster si chiama "interpolazione di stringhe", e viene affrontato altrove in questo thread.
CShark,

4
  1. La formattazione è il modo ".NET" di farlo. Alcuni strumenti di refactoring (Refactor! Per uno) proporranno addirittura di refactoring il codice di stile concat per utilizzare lo stile di formattazione.
  2. La formattazione è più facile da ottimizzare per il compilatore (anche se il secondo sarà probabilmente refactored per usare il metodo 'Concat' che è veloce).
  3. La formattazione di solito è più chiara da leggere (specialmente con una formattazione "elaborata").
  4. La formattazione significa chiamate implicite a ".ToString" su tutte le variabili, il che è utile per la leggibilità.
  5. Secondo "Efficace C #", le implementazioni .NET "WriteLine" e "Format" sono incasinate, autoboxing tutti i tipi di valore (il che è male). "Efficace C #" consiglia di eseguire chiamate ".ToString" in modo esplicito, che IMHO è falso (vedere la pubblicazione 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.

4

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

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

capisci il significato anche senza nomi di variabili, mentre il concat è ingombro di virgolette e + segni e confonde i miei 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 i nomi delle variabili, devo usare concat:

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

L'opzione di formattazione 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 è peggio. Rubino?

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

Prestazioni saggio, mi aspetto che l'opzione di formattazione di essere più lento poi il concat, in quanto formato richiede la stringa da analizzata . Non ricordo di aver dovuto ottimizzare questo tipo di istruzione, ma se lo facessi, guarderei stringmetodi come Concat()eJoin() .

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


4

Userei 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 a cui pensare. Dipende davvero da cosa serve la stringa.

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

Se contiene numeri o qualsiasi altra cosa non stringa (ad esempio date), userei String.Format perché mi dà un maggiore controllo sulla formattazione .

Se è per la creazione di una query come SQL, utilizzerei Linq .

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

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


3

Se hai a che fare con qualcosa che deve essere facile da leggere (e questo è la maggior parte del codice), resterei fedele alla versione di overload dell'operatore SOLO:

  • Il codice deve essere eseguito milioni di volte
  • Stai facendo tonnellate di concats (più di 4 è una tonnellata)
  • Il codice è indirizzato verso il Compact Framework

In almeno due di queste circostanze, utilizzerei invece StringBuilder.


3

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


2

Penso che ciò dipenda fortemente dalla complessità dell'output. Tendo a scegliere quale scenario funziona meglio al momento.

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


2

Preferisco anche la seconda, ma al momento non ho argomenti razionali a sostegno di tale posizione.


2

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 (credo 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

2
Richiede esattamente lo stesso tempo poiché le concatenazioni di stringhe basate sull'operatore vengono tradotte dal compilatore in chiamate string.Concat(...). Viene eseguito durante la compilazione, quindi non ha alcun impatto sulle prestazioni di runtime. Se esegui i test più volte o li esegui su campioni di test più grandi, vedrai che sono identici.
Allon Guralnek,

2

Dal momento che non credo che le risposte qui riguardino 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. Proverò a renderlo semplice.

Di 'che hai

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);

tmpqui non è in realtà una variabile locale, ma è temporanea per JIT (è inserita nello stack IL). Se si ldstrinserisce una stringa nello stack (come in IL per i letterali), si inserisce un riferimento a un puntatore di stringa nello stack.

Nel momento in cui chiami concat questo riferimento diventa un problema, perché non è disponibile alcun riferimento di 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 è possibile ridurre il numero di concatoperazioni?

Quindi, la risposta approssimativa è: string.Formatper> 1 concat, '+' funzionerà bene per 1 concat. E se non ti interessa fare ottimizzazioni di micro-prestazioni, string.Formatfunzionerà bene nel caso generale.

Una nota sulla cultura

E poi c'è qualcosa chiamato cultura ...

string.Formatti consente di utilizzare CultureInfonella formattazione. Un semplice operatore '+' utilizza la cultura corrente.

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

F.ex. considera cosa succede se cambi un '.' per un "," durante la scrittura del file con valori separati da virgola ... in olandese il separatore decimale è una virgola, quindi l'utente potrebbe semplicemente ricevere una sorpresa "divertente".

Risposta più dettagliata

Se non si conosce in anticipo la dimensione esatta della stringa, è consigliabile utilizzare una politica come questa per sovrallocare i buffer utilizzati. Lo spazio lento viene prima riempito, dopo di che i dati vengono copiati.

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 linea di fondo: crescere è un'operazione costosa.

Il modo più pratico per farlo è utilizzare una politica di sovrallocazione. La politica più comune è quella di sovrallocare i buffer con potenze di 2. Naturalmente, devi farlo un po 'più intelligente di quello (poiché non ha senso crescere da 1,2,4,8 se sai già che hai bisogno di 128 caratteri ) ma ottieni l'immagine. La politica garantisce che non siano necessarie troppe operazioni costose che ho descritto sopra.

StringBuilderè una classe che fondamentalmente sovralloca il buffer sottostante in potenze di due. string.Formatutilizza StringBuildersotto il cofano.

Questo rende la tua decisione un compromesso di base tra overallocate-and-append (-multiple) (w / wo culture) o semplicemente allocate-and-append.


1

Personalmente, il secondo come tutto quello che stai usando è nell'ordine diretto in cui verrà emesso. Considerando che con il primo devi abbinare {0} e {1} con il var corretto, che è facile da confondere.

Almeno non è così male come lo sprintf C ++ dove se sbagli il tipo di variabile sbaglierà tutto.

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


1

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


1

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


1

Oh, e solo per completezza, il seguente è un po 'più veloce della normale concatenazione:

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

1

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


1

Ero curioso di sapere dove si trovasse StringBuilder con questi test. Risultati di seguito ...

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 tick
Concat: 356 tick
Concat: 411 tick
Concat: 299 tick
Concat: 266 tick
Formato: 5269 tick
Formato: 954 tick
Formato: 1004 tick
Formato: 984 tick
Formato: 974 tick
StringBuilder: 629 tick
StringBuilder: 484 tick
StringBuilder: 482 tick
StringBuilder: 508 tick
StringBuilder: 504 tick

1

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.