Domanda

Supponiamo di avere uno stringbuilder in C# che fa questo:

StringBuilder sb = new StringBuilder();
string cat = "cat";
sb.Append("the ").Append(cat).(" in the hat");
string s = sb.ToString();

sarebbe altrettanto efficiente o più efficiente di avere:

string cat = "cat";
string s = String.Format("The {0} in the hat", cat);

Se sì, perché?

MODIFICARE

Dopo alcune risposte interessanti, mi sono reso conto che probabilmente avrei dovuto essere un po' più chiaro in quello che stavo chiedendo.Non stavo tanto chiedendo quale fosse più veloce nel concatenare una stringa, ma quale fosse più veloce iniettando una corda nell'altra.

In entrambi i casi sopra voglio inserire una o più stringhe nel mezzo di una stringa modello predefinita.

Dispiace per la confusione

È stato utile?

Soluzione

NOTA: Questa risposta è stata scritta quando .NET 2.0 era la versione corrente.Ciò potrebbe non essere più applicabile alle versioni successive.

String.Format utilizza a StringBuilder internamente:

public static string Format(IFormatProvider provider, string format, params object[] args)
{
    if ((format == null) || (args == null))
    {
        throw new ArgumentNullException((format == null) ? "format" : "args");
    }

    StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8));
    builder.AppendFormat(provider, format, args);
    return builder.ToString();
}

Il codice sopra è uno snippet di mscorlib, quindi la domanda diventa "is StringBuilder.Append() più veloce di StringBuilder.AppendFormat()"?

Senza il benchmarking probabilmente direi che l'esempio di codice riportato sopra verrebbe eseguito più rapidamente utilizzando .Append().Ma è un'ipotesi, prova a confrontare e/o profilare i due per ottenere un confronto adeguato.

Questo tizio, Jerry Dixon, ha fatto alcuni benchmark:

http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm

Aggiornato:

Purtroppo il collegamento sopra è morto da allora.Tuttavia c'è ancora una copia sulla Way Back Machine:

http://web.archive.org/web/20090417100252/http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm

Alla fine dipende se la formattazione della stringa verrà chiamata ripetutamente, ad es.stai eseguendo una seria elaborazione di testo su centinaia di megabyte di testo o se viene chiamato quando un utente fa clic su un pulsante di tanto in tanto.A meno che tu non stia eseguendo un enorme lavoro di elaborazione batch, resterei con String.Format, aiuta la leggibilità del codice.Se sospetti un collo di bottiglia delle prestazioni, applica un profiler sul tuo codice e vedi dove si trova realmente.

Altri suggerimenti

Dal Documentazione MSDN:

Le prestazioni di un'operazione di concatenazione per un oggetto String o StringBuilder dipendono dalla frequenza con cui si verifica un'allocazione di memoria.Un'operazione di concatenazione di stringhe alloca sempre memoria, mentre un'operazione di concatenazione StringBuilder alloca memoria solo se il buffer dell'oggetto StringBuilder è troppo piccolo per contenere i nuovi dati.Di conseguenza, la classe String è preferibile per un'operazione di concatenazione se viene concatenato un numero fisso di oggetti String.In tal caso, le singole operazioni di concatenazione potrebbero anche essere combinate in un'unica operazione dal compilatore.Un oggetto StringBuilder è preferibile per un'operazione di concatenazione se viene concatenato un numero arbitrario di stringhe;ad esempio, se un ciclo concatena un numero casuale di stringhe di input dell'utente.

Ho eseguito alcuni rapidi benchmark delle prestazioni e, per 100.000 operazioni in media su 10 esecuzioni, il primo metodo (String Builder) impiega quasi la metà del tempo del secondo (String Format).

Quindi, se questo è raro, non importa.Ma se si tratta di un'operazione comune, potresti voler utilizzare il primo metodo.

mi aspetterei String.Format per essere più lento: deve analizzare la stringa e Poi concatenarlo.

Un paio di note:

  • Formato è la strada da percorrere per le stringhe visibili all'utente nelle applicazioni professionali;questo evita bug di localizzazione
  • Se conosci in anticipo la lunghezza della stringa risultante, usa il metodo StringBuilder(Int32) costruttore per predefinire la capacità

Se non altro perché string.Format non fa esattamente quello che potresti pensare, ecco una ripetizione dei test 6 anni dopo su Net45.

Concat è ancora il più veloce ma in realtà la differenza è inferiore al 30%.StringBuilder e Format differiscono appena del 5-10%.Ho riscontrato variazioni del 20% eseguendo i test alcune volte.

Millisecondi, un milione di iterazioni:

  • Concatenazione:367
  • Nuovo stringBuilder per ogni chiave:452
  • StringBuilder memorizzato nella cache:419
  • stringa.Formato:475

La lezione che traggo è che la differenza di prestazioni è banale e quindi non dovrebbe impedirti di scrivere il codice leggibile più semplice possibile.Che per i miei soldi è spesso ma non sempre a + b + c.

const int iterations=1000000;
var keyprefix= this.GetType().FullName;
var maxkeylength=keyprefix + 1 + 1+ Math.Log10(iterations);
Console.WriteLine("KeyPrefix \"{0}\", Max Key Length {1}",keyprefix, maxkeylength);

var concatkeys= new string[iterations];
var stringbuilderkeys= new string[iterations];
var cachedsbkeys= new string[iterations];
var formatkeys= new string[iterations];

var stopwatch= new System.Diagnostics.Stopwatch();
Console.WriteLine("Concatenation:");
stopwatch.Start();

for(int i=0; i<iterations; i++){
    var key1= keyprefix+":" + i.ToString();
    concatkeys[i]=key1;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

Console.WriteLine("New stringBuilder for each key:");
stopwatch.Restart();

for(int i=0; i<iterations; i++){
    var key2= new StringBuilder(keyprefix).Append(":").Append(i.ToString()).ToString();
    stringbuilderkeys[i]= key2;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

Console.WriteLine("Cached StringBuilder:");
var cachedSB= new StringBuilder(maxkeylength);
stopwatch.Restart();

for(int i=0; i<iterations; i++){
    var key2b= cachedSB.Clear().Append(keyprefix).Append(":").Append(i.ToString()).ToString();
    cachedsbkeys[i]= key2b;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

Console.WriteLine("string.Format");
stopwatch.Restart();

for(int i=0; i<iterations; i++){
    var key3= string.Format("{0}:{1}", keyprefix,i.ToString());
    formatkeys[i]= key3;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

var referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway= concatkeys.Union(stringbuilderkeys).Union(cachedsbkeys).Union(formatkeys).LastOrDefault(x=>x[1]=='-');
Console.WriteLine(referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway);

Penso che nella maggior parte dei casi come questi la chiarezza, e non l’efficienza, dovrebbe essere la tua più grande preoccupazione.A meno che tu non stia schiacciando insieme tonnellate di corde o costruendo qualcosa per un dispositivo mobile meno potente, questo probabilmente non inciderà molto sulla tua velocità di corsa.

Ho scoperto che, nei casi in cui sto costruendo stringhe in modo abbastanza lineare, eseguire concatenazioni dirette o utilizzare StringBuilder è l'opzione migliore.Lo suggerisco nei casi in cui la maggior parte della stringa che stai costruendo è dinamica.Poiché molto poco testo è statico, la cosa più importante è che sia chiaro dove verrà inserita ogni parte di testo dinamico nel caso in cui debba essere aggiornato in futuro.

D'altra parte, se stai parlando di una grossa porzione di testo statico con due o tre variabili al suo interno, anche se è un po' meno efficiente, penso che la chiarezza che ottieni da string.Format ne valga la pena.L'ho usato all'inizio di questa settimana quando dovevo posizionare un pezzettino di testo dinamico al centro di un documento di 4 pagine.Sarà più semplice aggiornare quella grossa porzione di testo se è in un unico pezzo piuttosto che dover aggiornare tre pezzi concatenati insieme.

String.Format utilizza StringBuilder internamente... quindi logicamente questo porta all'idea che sarebbe un po' meno performante a causa di maggiori spese generali.Tuttavia, una semplice concatenazione di stringhe è il metodo più veloce per inserire una stringa tra altre due... in misura significativa.Questa prova è stata dimostrata da Rico Mariani nel suo primo Performance Quiz, anni fa.Il fatto semplice è che le concatenazioni... quando il numero di parti di stringa è noto (senza limitazioni... potresti concatenare mille parti... purché tu sappia che sono sempre 1000 parti)... sono sempre più veloci di StringBuilder o String.Format.Possono essere eseguiti con una singola allocazione di memoria e una serie di copie di memoria. Qui è la prova

Ed ecco il codice effettivo per alcuni metodi String.Concat, che alla fine chiamano FillStringChecked che utilizza puntatori per copiare la memoria (estratta tramite Reflector):

public static string Concat(params string[] values)
{
    int totalLength = 0;

    if (values == null)
    {
        throw new ArgumentNullException("values");
    }

    string[] strArray = new string[values.Length];

    for (int i = 0; i < values.Length; i++)
    {
        string str = values[i];
        strArray[i] = (str == null) ? Empty : str;
        totalLength += strArray[i].Length;

        if (totalLength < 0)
        {
            throw new OutOfMemoryException();
        }
    }

    return ConcatArray(strArray, totalLength);
}

public static string Concat(string str0, string str1, string str2, string str3)
{
    if (((str0 == null) && (str1 == null)) && ((str2 == null) && (str3 == null)))
    {
        return Empty;
    }

    if (str0 == null)
    {
        str0 = Empty;
    }

    if (str1 == null)
    {
        str1 = Empty;
    }

    if (str2 == null)
    {
        str2 = Empty;
    }

    if (str3 == null)
    {
        str3 = Empty;
    }

    int length = ((str0.Length + str1.Length) + str2.Length) + str3.Length;
    string dest = FastAllocateString(length);
    FillStringChecked(dest, 0, str0);
    FillStringChecked(dest, str0.Length, str1);
    FillStringChecked(dest, str0.Length + str1.Length, str2);
    FillStringChecked(dest, (str0.Length + str1.Length) + str2.Length, str3);
    return dest;
}

private static string ConcatArray(string[] values, int totalLength)
{
    string dest = FastAllocateString(totalLength);
    int destPos = 0;

    for (int i = 0; i < values.Length; i++)
    {
        FillStringChecked(dest, destPos, values[i]);
        destPos += values[i].Length;
    }

    return dest;
}

private static unsafe void FillStringChecked(string dest, int destPos, string src)
{
    int length = src.Length;

    if (length > (dest.Length - destPos))
    {
        throw new IndexOutOfRangeException();
    }

    fixed (char* chRef = &dest.m_firstChar)
    {
        fixed (char* chRef2 = &src.m_firstChar)
        {
            wstrcpy(chRef + destPos, chRef2, length);
        }
    }
}

Allora:

string what = "cat";
string inthehat = "The " + what + " in the hat!";

Godere!

Oh, inoltre, il più veloce sarebbe:

string cat = "cat";
string s = "The " + cat + " in the hat";

Dipende davvero.Per stringhe piccole con poche concatenazioni, in realtà è più veloce semplicemente aggiungere le stringhe.

String s = "String A" + "String B";

Ma per stringhe più grandi (stringhe molto molto grandi), è quindi più efficiente utilizzare StringBuilder.

In entrambi i casi sopra voglio inserire una o più stringhe nel mezzo di una stringa modello predefinita.

In tal caso, suggerirei che String.Format sia il più veloce perché è progettato per quello scopo esatto.

Dipende davvero dal tuo modello di utilizzo.
Un benchmark dettagliato tra string.Join, string,Concat E string.Format può essere trovato qui: String.Format non è adatto alla registrazione intensiva

Suggerirei di no, poiché String.Format non è stato progettato per la concatenazione, è stato progettato per formattare l'output di vari input come una data.

String s = String.Format("Today is {0:dd-MMM-yyyy}.", DateTime.Today);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top