StringBuilder'ın faydalarını anlıyorum.
Ama 2 dizgeyi birleştirmek istersem, bunu StringBuilder olmadan yapmanın daha iyi (daha hızlı) olduğunu varsayıyorum. Bu doğru mu?
StringBuilder'ı kullanmak hangi noktada (dizi sayısı) daha iyi hale gelir?
StringBuilder'ın faydalarını anlıyorum.
Ama 2 dizgeyi birleştirmek istersem, bunu StringBuilder olmadan yapmanın daha iyi (daha hızlı) olduğunu varsayıyorum. Bu doğru mu?
StringBuilder'ı kullanmak hangi noktada (dizi sayısı) daha iyi hale gelir?
Yanıtlar:
Jeff Atwood'un The Sad Tragedy of Micro-Optimization Theater'ı okumanızı şiddetle tavsiye ederim .
Basit Birleştirme ile StringBuilder'ı diğer yöntemlerle karşılaştırır.
Şimdi, bazı sayılar ve grafikler görmek istiyorsanız, bağlantıyı takip edin;)
Ancak 2 dizgeyi birleştirmek istersem, bunu StringBuilder olmadan yapmanın daha iyi (daha hızlı) olduğunu varsayıyorum. Bu doğru mu?
Bu gerçekten doğru, neden tam olarak açıklandığını şurada bulabilirsiniz:
http://www.yoda.arachsys.com/csharp/stringbuilder.html
Özetle: dizeleri tek seferde birleştirebiliyorsanız
var result = a + " " + b + " " + c + ..
StringBuilder olmadan sadece kopyalama yapıldığında daha iyi durumda olursunuz (ortaya çıkan dizgenin uzunluğu önceden hesaplanır.);
Gibi yapı için
var result = a;
result += " ";
result += b;
result += " ";
result += c;
..
her seferinde yeni nesneler oluşturulur, bu nedenle burada StringBuilder'ı düşünmelisiniz.
Sonunda makale şu temel kuralları özetliyor:
Başparmak Kuralları
Peki, StringBuilder'ı ne zaman kullanmalısınız ve dize birleştirme operatörlerini ne zaman kullanmalısınız?
Önemsiz olmayan bir döngüde birleştirirken kesinlikle StringBuilder kullanın - özellikle (derleme zamanında) döngü boyunca kaç yineleme yapacağınızı kesin olarak bilmiyorsanız. Örneğin, bir dosyayı her seferinde bir karakter okumak, devam ederken + = operatörünü kullanarak bir dize oluşturmak potansiyel olarak performans intiharıdır.
Tek bir ifadede birleştirilmesi gereken her şeyi (okunabilir bir şekilde) belirleyebildiğinizde, kesinlikle birleştirme operatörünü kullanın. (Birleştirilecek bir dizi şeyiniz varsa, String.Concat'i açıkça çağırmayı veya bir sınırlayıcıya ihtiyacınız varsa String.Join'i çağırmayı düşünün.)
Değişmez değerleri birkaç bitiştirilmiş bitlere bölmekten korkmayın - sonuç aynı olacaktır. Uzun bir harfi birkaç satıra bölerek okunabilirliğe yardımcı olabilirsiniz, örneğin, performansa zarar vermeden.
Birleştirme işleminin bir sonraki yinelemesini beslemekten başka bir şey için ara sonuçlara ihtiyacınız varsa, StringBuilder size yardımcı olmayacaktır. Örneğin, bir ad ve soyaddan tam bir ad oluşturursanız ve ardından üçüncü bir bilgi parçasını (takma ad, belki) eklerseniz, StringBuilder kullanmaktan yalnızca bunu yapmazsanız yararlanacaksınız. (ad + soyadı) dizesine başka bir amaçla ihtiyaç duyuyoruz (bir Person nesnesi oluşturan örnekte yaptığımız gibi).
Yapmanız gereken birkaç bitiştirmeniz varsa ve bunları gerçekten ayrı ifadeler halinde yapmak istiyorsanız, hangi yöne gittiğiniz önemli değildir. Hangi yolun daha verimli olduğu, birleştirme sayısına, ilgili dizenin boyutlarına ve hangi sırayla birleştirildiklerine bağlı olacaktır. Bu kod parçasının performans darboğazı olduğuna gerçekten inanıyorsanız, her iki şekilde de profil veya kıyaslama yapın.
System.String değişmez bir nesnedir - bu, içeriğini her değiştirdiğinizde yeni bir dizge tahsis edeceği ve bunun zaman (ve bellek?) Alacağı anlamına gelir. StringBuilder'ı kullanarak, yeni bir tane ayırmadan nesnenin gerçek içeriğini değiştirirsiniz.
Bu nedenle, dizede birçok değişiklik yapmanız gerektiğinde StringBuilder'ı kullanın.
Pek değil ... Eğer büyük dizgeleri birleştirirseniz veya bir döngüde olduğu gibi çok sayıda birleştirmeniz varsa StringBuilder'ı kullanmalısınız .
StringBuilderdöngü veya birleştirme özellikleri bir performans sorunu ise.
string s = "abcd", en azından bu son şey Duydum ki ... değişkenler söz konusu olduğunda büyük ihtimalle Concat olurdu.
a + "hello" + "somethingelse"ve hiç endişelenmedim. Sorun olacaksa, StringBuilder'ı kullanacağım. Ama ilk başta endişelenmedim ve yazmaya daha az zaman harcadım.
İşte konuyu kanıtlamak için basit bir test uygulaması:
class Program
{
static void Main(string[] args)
{
const int testLength = 30000;
var StartTime = DateTime.Now;
//TEST 1 - String
StartTime = DateTime.Now;
String tString = "test string";
for (int i = 0; i < testLength; i++)
{
tString += i.ToString();
}
Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
//result: 2000 ms
//TEST 2 - StringBuilder
StartTime = DateTime.Now;
StringBuilder tSB = new StringBuilder("test string");
for (int i = 0; i < testLength; i++)
{
tSB.Append(i.ToString());
}
Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
//result: 4 ms
Console.ReadLine();
}
}
Sonuçlar:
30'000 yineleme
1000 yineleme
500 yineleme
Kelimeleri ifade etmek
O halde üçe kadar sayacaksın, ne fazla ne az. Sayacağınız sayı üç olacak ve sayım sayısı üç olacaktır. Dördü saymayacaksın, ikisini de saymayacaksın, üçe geçmen dışında. Üçüncü sayı olan üç numaraya ulaşıldığında, Antakya'nın Kutsal El Bombasını atarsın.
Genellikle üç veya daha fazla dizginin birleştirilmesine neden olacak herhangi bir kod bloğu için dize oluşturucuyu kullanırım.
Kesin bir cevap yok, sadece pratik kurallar. Benim kişisel kurallarım şuna benzer:
StringBuilder.StringBuilder.StringBuilder.Ama 2 dizgeyi birleştirmek istersem, bunu StringBuilder olmadan yapmanın daha iyi ve daha hızlı olduğunu varsayıyorum. Bu doğru mu?
Evet. Ancak daha da önemlisi, bu tür durumlarda vanilya kullanmak çok daha okunaklıString . Öte yandan, bir döngüde kullanmak mantıklı ve aynı zamanda birleştirme kadar okunabilir de olabilir.
Belirli sayıda birleştirme işlemini bir eşik olarak belirten genel kurallara karşı dikkatli olurdum. Bunu döngülerde (ve yalnızca döngülerde) kullanmak muhtemelen aynı derecede yararlıdır, hatırlaması daha kolaydır ve daha mantıklıdır.
Bunun için fikirlerden etkilenmeyen veya bir gurur savaşı izlemeyen bir açıklama bulmak zor olduğundan, bunu kendim test etmek için LINQpad'e biraz kod yazmayı düşündüm.
İ.ToString () kullanmak yerine küçük boyutlu dizeler kullanmanın yanıt sürelerini değiştirdiğini buldum (küçük döngülerde görülebilir).
Test, zaman ölçümlerini makul şekilde karşılaştırılabilir aralıklarda tutmak için farklı yineleme dizileri kullanır.
En sonunda kodu kopyalayacağım, böylece kendiniz deneyebilirsiniz (results.Charts ... Dump () LINQPad dışında çalışmaz).
Çıktı (X Ekseni: Test edilen yineleme sayısı, Y Ekseni: Onaylarla alınan süre):
Yineleme sırası: 2, 3, 4, 5, 6, 7, 8, 9, 10

Yineleme sırası: 10, 20, 30, 40, 50, 60, 70, 80

Yineleme sırası: 100, 200, 300, 400, 500

Kod (LINQPad 5 kullanılarak yazılmıştır):
void Main()
{
Test(2, 3, 4, 5, 6, 7, 8, 9, 10);
Test(10, 20, 30, 40, 50, 60, 70, 80);
Test(100, 200, 300, 400, 500);
}
void Test(params int[] iterationsCounts)
{
$"Iterations sequence: {string.Join(", ", iterationsCounts)}".Dump();
int testStringLength = 10;
RandomStringGenerator.Setup(testStringLength);
var sw = new System.Diagnostics.Stopwatch();
var results = new Dictionary<int, TimeSpan[]>();
// This call before starting to measure time removes initial overhead from first measurement
RandomStringGenerator.GetRandomString();
foreach (var iterationsCount in iterationsCounts)
{
TimeSpan elapsedForString, elapsedForSb;
// string
sw.Restart();
var str = string.Empty;
for (int i = 0; i < iterationsCount; i++)
{
str += RandomStringGenerator.GetRandomString();
}
sw.Stop();
elapsedForString = sw.Elapsed;
// string builder
sw.Restart();
var sb = new StringBuilder(string.Empty);
for (int i = 0; i < iterationsCount; i++)
{
sb.Append(RandomStringGenerator.GetRandomString());
}
sw.Stop();
elapsedForSb = sw.Elapsed;
results.Add(iterationsCount, new TimeSpan[] { elapsedForString, elapsedForSb });
}
// Results
results.Chart(r => r.Key)
.AddYSeries(r => r.Value[0].Ticks, LINQPad.Util.SeriesType.Line, "String")
.AddYSeries(r => r.Value[1].Ticks, LINQPad.Util.SeriesType.Line, "String Builder")
.DumpInline();
}
static class RandomStringGenerator
{
static Random r;
static string[] strings;
public static void Setup(int testStringLength)
{
r = new Random(DateTime.Now.Millisecond);
strings = new string[10];
for (int i = 0; i < strings.Length; i++)
{
strings[i] = Guid.NewGuid().ToString().Substring(0, testStringLength);
}
}
public static string GetRandomString()
{
var indx = r.Next(0, strings.Length);
return strings[indx];
}
}
Birleştirme sayısını fiziksel olarak yazabildiğiniz sürece (a + b + c ...) bu büyük bir fark yaratmamalıdır. N kare (N = 10'da) 100X yavaşlamadır ve bu çok da kötü olmamalıdır.
Büyük sorun, yüzlerce dizeyi birleştirdiğiniz zamandır. N = 100'de 10000X kat yavaşlama elde edersiniz. Bu oldukça kötü.
Ne zaman kullanıp ne zaman kullanmamalı arasında ince bir çizgi olduğunu sanmıyorum. Tabii birisi altın koşullarla ortaya çıkmak için kapsamlı testler yapmadıysa.
Benim için, sadece 2 büyük dizgeyi birleştirirseniz StringBuilder kullanmayacağım. Belirsiz sayıma sahip bir döngü varsa, döngü küçük sayımlar olsa bile, muhtemelen yapacağım.
Tek bir birleştirme bir StringBuilder kullanmaya değmez. Genel bir kural olarak genellikle 5 birleştirme kullandım.