Este String.Format la fel de eficient ca StringBuilder

Să presupunem că am un constructor de caractere în C# care face acest lucru:

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

ar fi la fel de eficient sau mai eficient ca:

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

Dacă da, de ce?

EDIT

După câteva răspunsuri interesante, mi-am dat seama că probabil ar fi trebuit să fiu puțin mai clar în ceea ce am cerut. Nu mi-am cerut atât de mult ceea ce a fost mai rapid la concatenarea unui șir, dar care este mai rapid la injectarea un șir în altul.

În ambele cazuri de mai sus vreau să inject unuia sau mai multor șiruri de caractere în mijlocul unui șir șablon predefinit.

Ne pare rău pentru confuzie

0
fr hi bn
Lăsați aceste lucruri deschise pentru a permite îmbunătățiri viitoare.
adăugat autor Mark Biek, sursa
Într-un scenariu special, cel mai rapid nu este nici unul dintre acestea: în cazul în care piesa care urmează să fie înlocuită este egală cu dimensiunea părții noi, puteți schimba șirul în loc. Din păcate, acest lucru necesită reflecție sau cod nesigur și încalcă în mod deliberat imutabilitatea șirului. Nu este o practică bună, dar dacă viteza este o problemă ... :)
adăugat autor Abel, sursa
în exemplul de mai sus string s = "" + cat + "în pălărie"; ar putea fi cel mai rapid dacă nu este folosit într-o buclă, caz în care cel mai rapid va fi cu StringBuilder inițializat în afara bucla.
adăugat autor Surya Pratap, sursa

12 răspunsuri

Cred că în cele mai multe cazuri această claritate, și nu eficiența, ar trebui să fie cea mai mare preocupare. Cu excepția cazului în care zdrobiți împreună o mulțime de șiruri de caractere sau construiți ceva pentru un dispozitiv mobil cu o putere mai mică, probabil că aceasta nu va face prea mult din viteza dvs. de rulare.

Am descoperit că, în cazurile în care construiesc șiruri într-un mod destul de liniar, fie să faci concatenări drepte, fie să folosesc StringBuilder este cea mai bună opțiune. Vă sugerez acest lucru în cazurile în care majoritatea șirului pe care îl construiți este dinamic. Întrucât foarte puține dintre texte sunt statice, cel mai important lucru este că este clar în cazul în care fiecare piesă de text dinamic este pusă în cazul în care este nevoie de actualizare în viitor.

Pe de altă parte, dacă vorbim despre o bucată mare de text static cu două sau trei variabile în ea, chiar dacă este puțin mai eficientă, cred că claritatea pe care o câștigi din șir.Format o face să merite. Am folosit acest lucru la începutul acestei săptămâni când am plasat un text dinamic într-un document de 4 pagini. Va fi mai ușor să actualizați acea mare bucată de text dacă este într-o singură bucată decât să actualizați trei bucăți pe care le concatenați împreună.

0
adăugat
Da! Utilizați String.Format atunci când are sens să faceți acest lucru, adică când formatați șiruri de caractere. Utilizați concatenarea șirului sau un StringBuilder atunci când realizați concatenarea mecanică. Încercați întotdeauna să alegeți metoda care vă comunică intenția viitorului întreținător.
adăugat autor Rob, sursa

Nu mi-ar sugera, deoarece String.Format nu a fost conceput pentru concatenare, a fost proiectarea pentru formatarea ieșirii diferitelor intrări, cum ar fi o dată.

String s = String.Format("Today is {0:dd-MMM-yyyy}.", DateTime.Today);
0
adăugat

În ambele cazuri de mai sus, vreau să inject unuia sau mai multor șiruri de caractere în mijlocul unui șir șablon predefinit.

În acest caz, aș sugera String.Format este cel mai rapid, deoarece este design pentru acest scop exact.

0
adăugat

M-aș aștepta ca String.Format să fie mai lent - trebuie să parseze șirul și apoi să o concateze.

Cuplu de note:

  • Format is the way to go for user-visible strings in professional applications; this avoids localization bugs
  • If you know the length of the resultant string beforehand, use the StringBuilder(Int32) constructor to predefine the capacity
0
adăugat

Am rulat câteva repere de performanță rapide, iar pentru 100.000 de operațiuni în medie 10 run-uri, prima metodă (String Builder) durează aproape jumătate din timpul celui de-al doilea (format String).

Deci, dacă acest lucru este rar, nu contează. Dar dacă este o operațiune comună, atunci poate doriți să utilizați prima metodă.

0
adăugat

De asemenea, cel mai rapid ar fi:

string cat = "cat";
string s = "The " + cat + " in the hat";
0
adăugat
@Vaibhav este corect: în acest caz, concatenarea este cea mai rapidă. Bineînțeles, diferența ar fi nesemnificativă dacă nu s-ar fi repetat de multe ori, sau ar fi operat peste un șir mult mai mare.
adăugat autor Ben Collins, sursa
Cel mai rapid tip poate;)
adăugat autor UpTheCreek, sursa
nu, concatenarea șirului este extrem de lentă, deoarece .NET creează copii suplimentare ale variabilelor de șir între operațiile concate, în acest caz: două copii suplimentare plus copia finală pentru cesiune. Rezultat: performanță extrem de slabă în comparație cu StringBuilder care este făcută pentru optimizarea acestui tip de codificare în primul rând.
adăugat autor Abel, sursa
@Abel: Răspunsul ar putea fi lipsit de detalii, dar această abordare este cea mai rapidă opțiune, în acest exemplu. Compilatorul va transforma acest lucru într-un singur apel String.Concat (), astfel încât înlocuirea cu un StringBuilder va încetini efectiv codul.
adăugat autor Dan C., sursa

String.Format uses a StringBuilder internally:

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

Codul de mai sus este un fragment din mscorlib, astfel încât întrebarea devine " StringBuilder.Append() mai rapid decât StringBuilder.AppendFormat() "?

Fără evaluarea comparativă, probabil aș spune că mostra de cod de mai sus va funcționa mai repede folosind .Append() . Dar este o presupunere, încercați să comparați și / sau profilați cei doi pentru a obține o comparație corectă.

Acest tip, Jerry Dixon, a facut un anumit benchmarking:

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

Actualizat:

Din păcate, legătura de mai sus a murit de atunci. Cu toate acestea, există încă o copie pe Way Back Machine:

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

La sfârșitul zilei, depinde dacă formatul de șir va fi numit în mod repetat, adică faceți o prelucrare gravă a textului de peste 100 de megaocteți de text sau dacă este apelat atunci când un utilizator face clic pe un buton din când în când. Doar dacă nu faci niște procese de prelucrare în șarjă mare, aș rămâne cu String.Format, ajută la citirea codului. Dacă bănuiți că există o barieră de perfecționare, atunci lipiți un profil de cod și vedeți unde este într-adevăr.

0
adăugat
O problemă cu valorile de referință de pe pagina lui Jerry Dixon este că nu apelează niciodată .ToString() în obiectul StringBuilder . De-a lungul multor iterații, acel moment face o mare diferență și înseamnă că nu compară mere cu mere. Acesta este motivul pentru care arată o performanță atât de bună pentru StringBuilder și probabil explică surpriza lui. Tocmai am repetat benchmark-ul corectând această greșeală și am obținut rezultatele așteptate: operatorul String + a fost cel mai rapid, urmat de StringBuilder
adăugat autor Ben Collins, sursa
6 ani mai târziu, acest lucru nu mai este așa. În Net4, string.Format() creează și cachează o instanță StringBuilder pe care o reutilizează, astfel încât în ​​unele cazuri de testare ar putea fi mai rapidă decât StringBuilder. Am introdus un punct de referință revizuit în răspunsul de mai jos (care mai spune că concat este cel mai rapid și pentru cazul meu de testare, formatul este cu 10% mai lent decât StringBuilder).
adăugat autor Chris F Carroll, sursa

String.Format utilizează StringBuilder intern ... atât de logic încât duce la ideea că ar fi un pic mai puțin performant datorită mai multor cheltuieli generale. Cu toate acestea, o simplă concatenare a șirului este cea mai rapidă metodă de injectare a unui șir între alte două ... cu un grad semnificativ. Această dovadă a fost demonstrată de Rico Mariani în primul său test de performanță, cu mulți ani în urmă. Simplu este faptul că concatenările ... când numărul de părți de coarde este cunoscut (fără limitare .. ai putea concatena o mie de părți ... atâta timp cât știți întotdeauna 1000 părți) ... sunt întotdeauna mai rapide decât StringBuilder sau String.Format. Ele pot fi efectuate cu o singură alocare de memorie și o serie de copii de memorie. Aici este dovada

Și aici este codul actual pentru unele metode String.Concat, care în cele din urmă apel FillStringChecked care utilizează pointeri pentru a copia memorie (extras prin 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);
        }
    }
}

Deci:

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

Bucurați-vă!

0
adăugat
în Net4, string.Format cache-urile și reutilizează o instanță StringBuilder, astfel încât în ​​unele utilizări să fie mai rapidă.
adăugat autor Chris F Carroll, sursa

Depinde într-adevăr. Pentru șiruri mici cu câteva concatenări, este de fapt mai rapid doar să atașați șiruri de caractere.

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

Dar pentru șir mai mare (șiruri foarte foarte mari), atunci este mai eficient să folosiți StringBuilder.

0
adăugat

Din documentația MSDN :

Performanța unei operații de concatenare pentru un obiect String sau StringBuilder depinde de cât de des are loc o alocare a memoriei. O operație de concatenare a șirurilor alocă întotdeauna memoria, în timp ce o operație de concatenare StringBuilder alocă doar memorie dacă bufferul obiectului StringBuilder este prea mic pentru a se potrivi cu noile date. În consecință, clasa String este preferabilă pentru o operație de concatenare dacă un număr fix de obiecte String sunt concatenate. În acest caz, operațiile de concatenare individuale ar putea fi chiar combinate într-o singură operație de către compilator. Un obiect StringBuilder este preferabil pentru o operație de concatenare dacă un număr arbitrar de șiruri sunt concatenate; de exemplu, dacă o bucla concaterează un număr aleatoriu de șiruri de intrări de utilizator.

0
adăugat

Dacă numai pentru că string.Format nu face exact ceea ce ați putea crede, aici este o reluare a testelor de 6 ani mai târziu pe Net45.

Concat este încă mai rapid, dar într-adevăr este mai puțin de 30% diferență. StringBuilder și Format diferă cu doar 5-10%. Am variații de 20% care rulează testele de câteva ori.

Milisecunde, un milion de iterații:

  • Concatenare: 367
  • Un nou șir de caractere pentru fiecare cheie: 452
  • Cache StringBuilder: 419
  • șir.Format: 475

Lecția pe care o iau este că diferența de performanță este trivială și deci nu ar trebui să vă oprească să scrieți codul cel mai simplu de citit. Care pentru banii mei este adesea, dar nu întotdeauna 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; ix[1]=='-');
Console.WriteLine(referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway);
0
adăugat
De "string.Format nu face exact ceea ce ați putea crede" Vreau să spun că în codul sursă 4.5 încearcă să creeze și să reutilizeze o instanță stocată în StringBuilder. Așa că am inclus această abordare în test
adăugat autor Chris F Carroll, sursa

It really depends on your usage pattern.
A detailed benchmark between string.Join, string,Concat and string.Format can be found here: String.Format Isn't Suitable for Intensive Logging

0
adăugat