Output string: format atau concat dalam C #?


178

Katakanlah Anda ingin membuat string atau output. Manakah dari gaya berikut yang Anda sukai?

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

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

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

Apakah Anda lebih suka menggunakan format atau Anda hanya menggunakan string? Apa kesukaanmu? Apakah salah satu dari ini menyakiti matamu?

Apakah Anda punya argumen rasional untuk menggunakan satu dan bukan yang lain?

Saya akan memilih yang kedua.

Jawaban:


88

Coba kode ini.

Ini adalah versi kode Anda yang sedikit dimodifikasi.
1. Saya menghapus Console.WriteLine karena mungkin beberapa perintah lebih lambat dari yang saya coba ukur.
2. Saya memulai Stopwatch sebelum loop dan menghentikannya tepat setelah ini, dengan cara ini saya tidak kehilangan presisi jika fungsinya mengambil 26,4 ticks untuk dijalankan.
3. Cara Anda membagi hasil dengan beberapa iterasi salah. Lihat apa yang terjadi jika Anda memiliki 1000 milidetik dan 100 milidetik. Dalam kedua situasi, Anda akan mendapatkan 0 ms setelah membaginya dengan 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);

Itulah hasil saya:

1000000 x hasil = string.Format ("{0} {1}", p.FirstName, p.LastName); mengambil: 618ms - 2213706 kutu
1000000 x hasil = (p.FirstName + "" + p.LastName); mengambil: 166ms - 595610 ticks


1
Sangat menarik. Saya mendapat rata-rata 224ms vs 48ms, peningkatan x4.66, bahkan lebih baik dari x3.72 Anda. Saya bertanya-tanya apakah ada alat pasca-kompilasi yang dapat menulis ulang IL string.Formatyang tidak menggunakan fitur format komposit (yaitu hanya sederhana {0}) dan menggantinya dengan rangkaian string yang jauh lebih cepat. Saya ingin tahu hal seperti itu dapat dicapai dengan IL rewriter yang sudah ada seperti PostSharp.
Allon Guralnek

31
String tidak dapat diubah, ini berarti bagian memori yang sama digunakan berulang kali dalam kode Anda. Menambahkan dua string yang sama bersamaan dan membuat string baru yang sama berulang-ulang tidak memengaruhi memori. Net cukup pintar hanya untuk menggunakan referensi memori yang sama. Karenanya kode Anda tidak benar-benar menguji perbedaan antara dua metode concat. Lihat kode dalam jawaban saya di bawah ini.
Ludington

1
Jujur, saya selalu menyatukan karena lebih mudah dibaca untuk saya dan wow lebih cepat :)
puretppc

Jadi kecepatan adalah satu-satunya alasan untuk memilih satu dari yang lain?
niico

158

Saya kagum bahwa begitu banyak orang segera ingin menemukan kode yang mengeksekusi tercepat. Jika SATU JUTAAN pengulangan MASIH membutuhkan waktu kurang dari satu detik untuk diproses, apakah ini akan ADA CARA APA yang terlihat oleh pengguna akhir? Sangat tidak mungkin.

Optimalisasi prematur = GAGAL.

Saya akan String.Formatmemilih, hanya karena itu paling masuk akal dari sudut pandang arsitektur. Saya tidak peduli tentang kinerja sampai menjadi masalah (dan jika itu terjadi, saya akan bertanya pada diri sendiri: Apakah saya perlu menggabungkan satu juta nama sekaligus? Tentunya mereka tidak akan cocok di layar ...)

Pertimbangkan apakah pelanggan Anda kemudian ingin mengubahnya sehingga mereka dapat mengonfigurasi apakah akan menampilkan "Firstname Lastname"atau "Lastname, Firstname."Dengan opsi Format, ini mudah - cukup tukar keluar string format. Dengan concat, Anda akan memerlukan kode tambahan. Tentu itu tidak terdengar seperti masalah besar dalam contoh khusus ini tetapi memperkirakan.


47
Poin bagus dalam hal "Optimasi Prematur == GAGAL", ya. Tetapi, ketika Anda mulai membayar untuk jejak pelaksanaan (cloud dan infrastruktur sebagai layanan, ada orang?) Dan / atau Anda mulai mendukung 1 juta pengguna pada sesuatu maka respons terhadap satu pengguna pada permintaan bukanlah pertanyaan. Biaya untuk melayani permintaan kepada pengguna adalah biaya untuk bottom line Anda serta masalah skala jika / ketika beberapa ribu panggilan lain datang melalui ...
Aidanapword

23
Ini sama sekali salah. Dalam lingkungan pengembangan web, sering kali kode pembuatan string Anda akan jauh di dalam baik model, tampilan dan pengontrol Anda dan bisa dipanggil puluhan ribu kali per beban halaman. Mengurangi lamanya waktu yang dihabiskan untuk mengevaluasi kode pembuatan string sebesar 50% bisa menjadi kemenangan besar.
Benjamin Sussman

2
Pertanyaan seperti ini tidak hanya akan berlaku dalam satu contoh OP. Jawabannya adalah hal yang dapat diingat orang sebagai "ke arah mana saya harus mengumpulkan string?" ketika mereka menulis semua kode mereka.
Phil Miller

6
@Benjamin: ... dalam hal ini, Anda akan membuat profil dan menemukan itu menjadi hambatan Anda. Saya berani bertaruh uang bahwa Anda baru saja menariknya entah dari mana; setelah menulis dan membuat profil sejumlah aplikasi web di masa lalu, saya hampir selalu menemukan hambatan dalam waktu respons (di sisi server) sebagai permintaan basis data.
BlueRaja - Danny Pflughoeft

2
Ini jelas BUKAN pengoptimalan prematur. Cukup keliru. Kinerja string dapat benar-benar menghentikan UI, terutama di .NET jika Anda melakukan banyak pemformatan dan pembuatan String. ubiquity.acm.org/article.cfm?id=1513451
user99999991

54

Oh sayang - setelah membaca salah satu balasan saya mencoba membalik urutan operasi - jadi melakukan penggabungan terlebih dahulu, kemudian String.

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

Jadi urutan operasi membuat perbedaan BESAR, atau lebih tepatnya operasi pertama SELALU jauh lebih lambat.

Berikut ini adalah hasil dari proses di mana operasi diselesaikan lebih dari satu kali. Saya telah mencoba mengubah pesanan tetapi umumnya mengikuti aturan yang sama, setelah hasil pertama diabaikan:

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

Seperti yang Anda lihat selanjutnya menjalankan metode yang sama (saya refactored kode menjadi 3 metode) secara bertahap lebih cepat. Yang tercepat tampaknya adalah metode Console.WriteLine (String.Concat (...)), diikuti oleh penggabungan normal, dan kemudian operasi yang diformat.

Penundaan awal dalam startup kemungkinan merupakan inisialisasi Stream Konsol, seperti menempatkan Console.Writeline ("Mulai!") Sebelum operasi pertama membawa semua waktu kembali ke jalurnya.


2
Kemudian hapus Console.WriteLine sepenuhnya dari tes Anda. Itu condong hasilnya!
CShark

Saya selalu mulai dengan skenario throwaway atau "control" ketika menjalankan tes kinerja untuk alasan yang tepat ini
drzaus

36

String tidak dapat diubah, ini berarti bagian memori yang sama digunakan berulang kali dalam kode Anda. Menambahkan dua string yang sama bersamaan dan membuat string baru yang sama berulang kali tidak memengaruhi memori. Net cukup pintar hanya untuk menggunakan referensi memori yang sama. Karenanya kode Anda tidak benar-benar menguji perbedaan antara dua metode concat.

Coba ini untuk ukuran:

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 sampel:

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
Menambahkan StringBuilder dan contoh keluaran ke jawabannya
mikeschuld

Saya melihat bagaimana menggunakan string.Formatbernilai kinerja kecil di sini. Secara arsitektur lebih baik karena ini berarti Anda dapat mengubah format lebih mudah. Tapi pembuat string saya benar-benar tidak mengerti. Setiap utas lainnya di sini mengatakan Anda harus menggunakan Stringbuilder alih-alih string penggabung. Apa untungnya? Jelas bukan kecepatan, karena tolok ukur ini terbukti.
roryok

22

Kasihan penerjemah yang malang

Jika Anda tahu aplikasi Anda akan tetap dalam bahasa Inggris, maka baiklah, simpan kutu jam. Namun, banyak budaya biasanya melihat Lastname Firstname di, misalnya, alamat.

Jadi gunakan string.Format(), terutama jika Anda ingin aplikasi Anda pergi ke mana saja bahwa bahasa Inggris bukan bahasa pertama.


2
Bagaimana string.Format()berperilaku berbeda dalam budaya yang berbeda? Bukankah masih mencetak nama depan kemudian nama belakang? Sepertinya Anda harus mempertimbangkan budaya yang berbeda di kedua situasi. Saya merasa seperti kehilangan sesuatu di sini.
Broots Waymb

2
Saya setuju dengan @BahayaZone .. bagaimana string.Format()Anda tahu Anda menggunakan nama untuk alamat? Jika string.Format()ditukar {0} {1}berdasarkan budaya, saya akan menganggapnya rusak.
Alex McMillan

2
Saya percaya poin yang Jeremy coba buat adalah bahwa dalam skenario yang dijelaskan untuk mendukung berbagai negara, mungkin pantas untuk mengekstrak string format itu sendiri ke sumber daya bahasa. Untuk sebagian besar negara, string adalah "{0} {1}", tetapi untuk negara-negara di mana nama keluarga-pertama adalah operasi tipikal (misalnya Hungaria, Hong Kong, Kamboja, Cina, Jepang, Korea, Madagaskar, Taiwan, Vietnam, dan bagian dari India) string itu akan menjadi "{1} {0}" sebagai gantinya.
Richard J Foster

Memang. Atau, lebih halus, tambahkan string format sebagai atribut orang tersebut. Saya, misalnya, suka memiliki nama keluarga setelah nama depan saya, tetapi rekan kerja saya Beng tidak.
Jeremy McGee

14

Inilah hasil saya lebih dari 100.000 iterasi:

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

Dan di sini adalah kode bangku:

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

Jadi, saya tidak tahu jawaban siapa yang ditandai sebagai jawaban :)


Mengapa latar belakang biru untuk jawaban ini?
user88637

@Yossi itu biru karena penjawabnya sama dengan penanya
Davy8.

9

Senar gabungan baik-baik saja dalam skenario sederhana seperti itu - lebih rumit dengan hal yang lebih rumit dari itu, bahkan LastName, FirstName. Dengan format yang dapat Anda lihat, sekilas, seperti apa struktur akhir string ketika membaca kode, dengan penggabungan hampir mustahil untuk segera melihat hasil akhir (kecuali dengan contoh yang sangat sederhana seperti ini).

Apa artinya itu dalam jangka panjang adalah ketika Anda kembali untuk membuat perubahan pada format string Anda, Anda akan memiliki kemampuan untuk masuk dan membuat beberapa penyesuaian pada string format, atau mengerutkan alis Anda dan mulai bergerak di sekitar semua jenis aksesor properti dicampur dengan teks, yang lebih cenderung menimbulkan masalah.

Jika Anda menggunakan .NET 3.5, Anda dapat menggunakan metode ekstensi seperti ini dan mudah mengalir, dari sintaks manset seperti ini:

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

Akhirnya, ketika aplikasi Anda tumbuh dalam kompleksitas Anda dapat memutuskan bahwa untuk menjaga string dalam aplikasi Anda, Anda ingin memindahkannya ke file sumber daya untuk melokalisasi atau hanya menjadi pembantu statis. Ini akan menjadi JAUH lebih mudah untuk dicapai jika Anda memiliki format yang digunakan secara konsisten, dan kode Anda dapat dengan mudah di refactored untuk menggunakan sesuatu seperti

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

7

Untuk manipulasi yang sangat sederhana, saya akan menggunakan gabungan, tetapi setelah Anda melampaui 2 atau 3 elemen, Format menjadi IMO yang lebih tepat.

Alasan lain untuk memilih String.Format adalah bahwa .NET string tidak dapat diubah dan melakukannya dengan cara ini menghasilkan lebih sedikit salinan sementara / perantara.


6

Sementara saya benar-benar memahami preferensi gaya dan memilih rangkaian untuk jawaban pertama saya sebagian berdasarkan pada preferensi saya sendiri, sebagian dari keputusan saya didasarkan pada pemikiran bahwa rangkaian akan lebih cepat. Jadi, karena penasaran, saya mengujinya dan hasilnya mengejutkan, terutama untuk string kecil.

Menggunakan kode berikut:

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

Saya mendapat hasil sebagai berikut:

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

Menggunakan metode pemformatan lebih dari 100 kali lebih lambat !! Rangkaian bahkan tidak mendaftar sebagai 1 ms, itulah sebabnya saya output ticks timer juga.


2
Namun tentu saja Anda harus melakukan operasi lebih dari satu kali untuk mendapatkan pengukuran.
erikkallen

2
Dan kehilangan panggilan ke Console.Writeline () karena itu berada di luar cakupan pertanyaan?
Aidanapword

apakah Anda menguji dengan pembuat string? ;)
niico

6

Mulai dari string interpolasi C # 6.0 dapat digunakan untuk melakukan ini, yang semakin menyederhanakan format.

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

Ekspresi string terinterpolasi tampak seperti string template yang berisi ekspresi. Ekspresi string terinterpolasi menciptakan string dengan mengganti ekspresi yang terkandung dengan representasi ToString dari hasil ekspresi.

String yang diinterpolasi memiliki kinerja yang mirip dengan String.Format, tetapi peningkatan keterbacaan dan sintaksis yang lebih pendek, karena fakta bahwa nilai dan ekspresi dimasukkan secara in-line.

Silakan juga merujuk ke artikel dotnetperls ini tentang interpolasi string.

Jika Anda mencari cara default untuk memformat string Anda, ini masuk akal dalam hal keterbacaan dan kinerja (kecuali jika mikrodetik akan membuat perbedaan dalam use case spesifik Anda).


5

Untuk penggabungan string dasar, saya biasanya menggunakan gaya kedua - lebih mudah dibaca dan lebih sederhana. Namun, jika saya melakukan kombinasi string yang lebih rumit, saya biasanya memilih String.Format.

String.Format menghemat banyak kutipan dan plus ...

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

Hanya beberapa kereta yang diselamatkan, tapi saya pikir, dalam contoh ini, format membuatnya jauh lebih bersih.


5

Tes yang lebih baik adalah mengawasi memori Anda menggunakan penghitung Perfmon dan CLR. Pemahaman saya adalah bahwa seluruh alasan Anda ingin menggunakan String.Format alih-alih hanya menyatukan string adalah, karena string tidak berubah, Anda tidak perlu membebani pengumpul sampah dengan string sementara yang perlu direklamasi di pass berikutnya.

StringBuilder dan String.Format, meskipun berpotensi lebih lambat, lebih hemat memori.

Apa yang sangat buruk tentang penggabungan string?


Saya setuju; setiap operasi string membuat salinan string yang baru. Semua memori itu akan direklamasi oleh pemulung cepat atau lambat. Jadi, mengalokasikan banyak string dapat kembali menggigit Anda nanti.
Marnix van Valen

5

Secara umum saya lebih suka yang pertama, karena terutama ketika string menjadi panjang bisa lebih mudah dibaca.

Manfaat lainnya adalah saya percaya salah satu kinerja, karena yang terakhir benar-benar melakukan 2 pernyataan penciptaan string sebelum meneruskan string terakhir ke metode Console.Write. String.Format menggunakan StringBuilder di bawah selimut yang saya yakini, sehingga beberapa konvergensi dihindari.

Namun perlu dicatat bahwa jika parameter yang Anda berikan ke String.Format (dan metode lain seperti Console.Write) adalah tipe nilai maka mereka akan dimasukkan dalam kotak sebelum diteruskan, yang dapat memberikan hit kinerjanya sendiri. Posting blog di sini .


1
Posting blog itu sekarang di: jeffbarnes.net/blog/post/2006/08/08/… . Saya menderita kekurangan rep untuk mengedit.
Richard Slater

5

Seminggu dari sekarang 19 Agustus 2015, pertanyaan ini akan berusia tepat tujuh (7) tahun. Sekarang ada cara yang lebih baik untuk melakukan ini. Lebih baik dalam hal rawatan karena saya belum melakukan tes kinerja apa pun dibandingkan dengan hanya string gabungan (tetapi apakah itu penting akhir-akhir ini? Perbedaan beberapa milidetik?). Cara baru melakukannya dengan C # 6.0 :

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

Fitur baru ini lebih baik , IMO, dan sebenarnya lebih baik dalam kasus kami karena kami memiliki kode tempat kami membuat querystrings yang nilainya tergantung pada beberapa faktor. Bayangkan satu querystring di mana kita memiliki 6 argumen. Jadi alih-alih melakukan, misalnya:

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

di dapat ditulis seperti ini dan lebih mudah dibaca:

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

Memang, cara baru C # 6.0 lebih baik daripada alternatif sebelumnya - setidaknya dari sudut pandang keterbacaan.
Philippe

Betul sekali. Dan itu juga lebih aman karena Anda tidak perlu khawatir tentang objek mana yang masuk ke indeks (pengganti) karena Anda akan langsung meletakkan objek di tempat yang Anda inginkan.
von v.

BTW, sebenarnya memanggil Format (setidaknya dengan Roslyn).
Philippe

BTW, apa yang dimaksud poster ini disebut "string interpolasi", dan dibahas di tempat lain di utas ini.
CShark

4
  1. Memformat adalah cara ".NET" untuk melakukannya. Alat refactoring tertentu (Refactor! For one) bahkan akan mengusulkan untuk refactor kode gaya concat untuk menggunakan gaya format.
  2. Pemformatan lebih mudah dioptimalkan untuk kompiler (meskipun yang kedua mungkin akan di-refactored untuk menggunakan metode 'Concat' yang cepat).
  3. Pemformatan biasanya lebih jelas untuk dibaca (terutama dengan pemformatan "mewah").
  4. Memformat berarti panggilan implisit ke '.ToString' pada semua variabel, yang bagus untuk dibaca.
  5. Menurut "Efektif C #", implementasi .NET 'WriteLine' dan 'Format' kacau, mereka autobox semua jenis nilai (yang buruk). "C # Efektif" menyarankan untuk melakukan panggilan '.ToString' secara eksplisit, yang IMHO adalah palsu (lihat posting Jeff )
  6. Saat ini, petunjuk tipe pemformatan tidak dicentang oleh kompiler, menghasilkan kesalahan runtime. Namun, ini dapat diubah di versi mendatang.

4

Saya memilih berdasarkan keterbacaan. Saya lebih suka opsi format ketika ada beberapa teks di sekitar variabel. Dalam contoh ini:

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

Anda memahami artinya bahkan tanpa nama variabel, sedangkan konser itu penuh dengan tanda kutip dan + tanda dan membingungkan mata saya:

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

(Saya meminjam contoh Mike karena saya suka)

Jika string format tidak banyak berarti tanpa nama variabel, saya harus menggunakan concat:

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

Opsi format membuat saya membaca nama variabel dan memetakannya ke nomor yang sesuai. Opsi concat tidak mengharuskan itu. Saya masih bingung dengan tanda kutip dan tanda +, tetapi alternatifnya lebih buruk. Rubi?

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

Dari segi kinerja, saya berharap opsi format menjadi lebih lambat daripada concat, karena format membutuhkan string untuk diuraikan . Saya tidak ingat harus mengoptimalkan instruksi semacam ini, tetapi jika saya lakukan, saya akan melihat stringmetode seperti Concat()danJoin() .

Keuntungan lain dengan format adalah bahwa string format dapat dimasukkan ke dalam file konfigurasi. Sangat berguna dengan pesan kesalahan dan teks UI.


4

Saya akan menggunakan String.Format, tetapi saya juga akan memiliki string format dalam file sumber daya sehingga dapat dilokalisasi untuk bahasa lain. Menggunakan concat string sederhana tidak memungkinkan Anda untuk melakukan itu. Jelas jika Anda tidak perlu melokalisasi string itu, ini bukan alasan untuk dipikirkan. Ini benar-benar tergantung pada untuk apa string itu.

Jika itu akan ditunjukkan kepada pengguna, saya akan menggunakan String.Format sehingga saya bisa melokalkan jika saya perlu - dan FxCop akan mengeja memeriksanya untuk saya, kalau-kalau :)

Jika ini berisi angka atau hal-hal non-string lainnya (misalnya tanggal), saya akan menggunakan String.Format karena itu memberi saya lebih banyak kontrol atas pemformatan .

Jika untuk membangun kueri seperti SQL, saya akan menggunakan Linq .

Jika untuk merangkai string di dalam satu lingkaran, saya akan menggunakan StringBuilder untuk menghindari masalah kinerja.

Jika itu untuk beberapa output pengguna tidak akan melihat, dan tidak akan mempengaruhi kinerja saya akan menggunakan String.Format karena saya sudah terbiasa menggunakannya dan saya hanya terbiasa :)


3

Jika Anda berurusan dengan sesuatu yang perlu mudah dibaca (dan ini adalah kode terbanyak), saya akan tetap menggunakan versi overload operator KECUALI:

  • Kode perlu dieksekusi jutaan kali
  • Anda melakukan banyak konser (lebih dari 4 adalah satu ton)
  • Kode ditargetkan ke Compact Framework

Di bawah setidaknya dua dari keadaan ini, saya akan menggunakan StringBuilder sebagai gantinya.


3

Jika Anda bermaksud melokalisasi hasil, maka String.Format sangat penting karena bahasa alami yang berbeda bahkan mungkin tidak memiliki data dalam urutan yang sama.


2

Saya pikir ini sangat tergantung pada seberapa kompleks outputnya. Saya cenderung memilih skenario mana yang paling berhasil saat itu.

Pilih alat yang tepat berdasarkan pekerjaan: D Mana yang terlihat paling bersih!


2

Saya lebih suka yang kedua juga, tetapi saya tidak punya argumen rasional saat ini untuk mendukung posisi itu.


2

Yang bagus!

Baru ditambahkan

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

Dan itu bahkan lebih cepat (saya kira string.Compat dipanggil dalam kedua contoh, tetapi yang pertama membutuhkan semacam terjemahan).

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
Dibutuhkan jumlah waktu yang persis sama sejak rangkaian string berbasis operator diterjemahkan oleh kompilator ke panggilan string.Concat(...). Ini dilakukan selama kompilasi sehingga tidak berdampak pada kinerja run-time. Jika Anda menjalankan tes Anda beberapa kali atau menjalankannya pada sampel tes yang lebih besar Anda akan melihat mereka identik.
Allon Guralnek

2

Karena saya tidak berpikir jawabannya di sini mencakup semuanya, saya ingin membuat tambahan kecil di sini.

Console.WriteLine(string format, params object[] pars)panggilan string.Format. Tanda '+' menyiratkan penggabungan string. Saya tidak berpikir ini selalu ada hubungannya dengan gaya; Saya cenderung untuk mencampur dua gaya tergantung pada konteks saya.

Jawaban singkat

Keputusan yang Anda hadapi berkaitan dengan alokasi string. Saya akan mencoba membuatnya sederhana.

Katakan sudah

string s = a + "foo" + b;

Jika Anda menjalankan ini, itu akan mengevaluasi sebagai berikut:

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

tmpdi sini sebenarnya bukan variabel lokal, tetapi ini adalah sementara untuk JIT (didorong pada IL stack). Jika Anda mendorong string pada tumpukan (sepertildstr dalam IL untuk literal), Anda meletakkan referensi ke pointer string pada stack.

Saat Anda menelepon concat referensi ini menjadi masalah, karena tidak ada referensi string yang tersedia yang mengandung kedua string. Ini berarti bahwa. NET perlu mengalokasikan blok memori baru, dan kemudian mengisinya dengan dua string. Alasan ini menjadi masalah, adalah karena alokasi relatif mahal.

Yang mengubah pertanyaan menjadi: Bagaimana Anda bisa mengurangi jumlah concatoperasi?

Jadi, jawaban kasarnya adalah: string.Formatuntuk> 1 concat, '+' akan bekerja dengan baik untuk 1 concat. Dan jika Anda tidak peduli melakukan optimasi kinerja mikro, string.Formatakan berfungsi dengan baik dalam kasus umum.

Catatan tentang Budaya

Dan kemudian ada sesuatu yang disebut budaya ...

string.Formatmemungkinkan Anda untuk digunakan CultureInfodalam pemformatan Anda. Operator sederhana '+' menggunakan budaya saat ini.

Ini terutama komentar penting jika Anda sedang menulis format file dan f.ex. doublenilai yang Anda 'tambahkan' ke string. Pada mesin yang berbeda, Anda mungkin berakhir dengan string yang berbeda jika Anda tidak menggunakannya string.Formatdengan eksplisit CultureInfo.

F.ex. pertimbangkan apa yang terjadi jika Anda mengubah '.' untuk ',' saat menulis file koma-terpisah-nilai Anda ... dalam bahasa Belanda pemisah desimal adalah koma, sehingga pengguna Anda mungkin hanya akan mendapatkan kejutan 'lucu'.

Jawaban yang lebih jelas

Jika Anda tidak tahu ukuran pasti dari string sebelumnya, yang terbaik adalah menggunakan kebijakan seperti ini untuk secara keseluruhan menempatkan buffer yang Anda gunakan. Ruang kendur pertama kali diisi, setelah itu data disalin.

Berkembang berarti mengalokasikan blok memori baru dan menyalin data lama ke buffer baru. Blok memori lama kemudian bisa dilepaskan. Anda mendapatkan intinya pada saat ini: pertumbuhan adalah operasi yang mahal.

Cara paling praktis untuk melakukan ini adalah dengan menggunakan kebijakan penempatan secara keseluruhan. Kebijakan yang paling umum adalah menempatkan buffer secara keseluruhan dalam kekuatan 2. Tentu saja, Anda harus melakukannya sedikit lebih pintar dari itu (karena tidak masuk akal untuk tumbuh dari 1,2,4,8 jika Anda sudah tahu Anda membutuhkan 128 karakter) ) Tapi kamu mendapatkan gambarnya. Kebijakan ini memastikan Anda tidak perlu terlalu banyak operasi mahal yang saya jelaskan di atas.

StringBuilderadalah kelas yang pada dasarnya secara keseluruhan menempatkan buffer yang mendasari dalam kekuatan dua. string.Formatgunakan di StringBuilderbawah tenda.

Hal ini membuat keputusan Anda menjadi trade-off dasar antara keseluruhan-dan-tambahkan (-multiple) (dengan budaya) atau hanya mengalokasikan-dan-menambahkan.


1

Secara pribadi, yang kedua karena semua yang Anda gunakan adalah dalam urutan langsung, maka akan menjadi output. Sedangkan dengan yang pertama Anda harus mencocokkan {0} dan {1} dengan var yang tepat, yang mudah berantakan.

Setidaknya itu tidak seburuk sprintf C ++ di mana jika Anda mendapatkan tipe variabel yang salah semuanya akan meledak.

Juga, karena yang kedua semuanya inline dan tidak harus melakukan pencarian dan mengganti semua {0} hal-hal, yang terakhir harus lebih cepat ... meskipun saya tidak tahu pasti.


1

Saya sebenarnya suka yang pertama karena ketika ada banyak variabel yang bercampur dengan teks itu tampaknya lebih mudah dibaca untuk saya. Plus, lebih mudah untuk berurusan dengan tanda kutip ketika menggunakan string. Format (), eh, format. Berikut adalah analisis rangkaian string yang layak .


1

Saya selalu menggunakan rute string.Format (). Mampu menyimpan format dalam variabel seperti contoh Nathan adalah keuntungan besar. Dalam beberapa kasus saya dapat menambahkan variabel tetapi sekali lebih dari 1 variabel sedang disatukan saya menolak untuk menggunakan format.


1

Oh, dan hanya untuk kelengkapan, berikut ini adalah beberapa kutu lebih cepat daripada penggabungan normal:

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

1

Yang pertama (format) terlihat lebih baik bagi saya. Ini lebih mudah dibaca dan Anda tidak membuat objek string sementara tambahan.


1

Saya ingin tahu di mana StringBuilder berdiri dengan tes-tes ini. Hasil di bawah ini ...

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

Hasil:

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

1

Menurut bahan persiapan MCSD, Microsoft menyarankan menggunakan operator + ketika berhadapan dengan sejumlah kecil gabungan (mungkin 2 hingga 4). Saya masih tidak yakin mengapa, tapi itu sesuatu yang perlu dipertimbangkan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.