Skip Navigation LinksAnasayfa > C# : Efektif string birleştirme teknikleri

C# : Efektif string birleştirme teknikleri

Bu yazı .net framework altında etkili string birleştirme yöntemlerine değinmektedir.

Bildiğiniz gibi String değişkeni yazılımlarımızda çok sık kullandığımız veri tiplerimizden bir tanesidir. .Net Framework, System.Text uzayı (namespace) altında barındırdığı sınıflarla (class) string işlemlerine zengin bir destek sağlar. System.Text altındaki sınıfların ayrıntılarına buradan erişebilirsiniz. Mutlaka göz atmanızı tavsiye ediyorum.

Asıl konumuza gelince, bazen yazılımlarımız içerisinde farklı değişkenleri birleştirerek text çıktıları oluşturmamız gerekir. Örneğin, uygulamanızın ürettiği hataları yakalayarak ayrıntılarını bir log dosyasına yazdırmak isteyebilirsiniz. Bu durumda uygulamanız oluşan hatayı yakalamalı ve hata nesnesinin tüm özelliklerini bir metin haline getirerek log dosyasına yazmalıdır. (ayrıca satır satır veri yazma işlemi yapmak da mümkündür; fakat konumuz string birleştirme yöntemlerinin performasını irdelemek olduğu için verinin birleştirilerek yazılması esas alınmıştır.)

Bu yazıda 3 farklı yöntemi inceleyeceğiz ve test edeceğiz.
  • "+" yöntemi ile birleştirme
  • System.Text.StringBuilder sınıfı ile birleştirme
  • System.Collections.Generic.List Generic sınıfı ile birleştirme

1. "+" yöntemi ile birleştirme.

Bu metod string değişkenlerinin + operatörü ile birbiri ardına eklenmesi esasına dayanır.

// iki farklı değişken birleştirilmiştir.
string strFirstName = "Cihan";
string strLastName = "Uçar";
string strName = strFirstName + " " + strLastName;

// aynı değişkene yeni veri eklenmektedir.
string myMessage = "Merhaba ";
myMessage = myMessage + "Dünya. "; // + ile ekleniyor. (Kötünün kötüsü kod.)
myMessage += "Küresel ısınma dünyanın sonu mu?"; // += ile ekleniyor.
myMessage += "Buzullar eriyor, sular yükseliyor.";



Bu yönteme dair görüşümü çok net belirtmek istiyorum: Bu yöntemi unutun, hafızanızdan kazıyın! Bu yöntem string birleştirme işlemleri için son derece kötü ve performans düşürücü bir yöntemdir. Bu yöntemdeki dezavantaj şudur :

String objesi immutable (değişmeyen) bir objedir ve siz her seferinde + ile yeni bir string eklediğinizde mevcut string'iniz yeni bir objeye atanmaktadır. Satır satır açıklamak gerekirse :

string myMessage = "Merhaba ";


myMessage objesi ilk defa yaratıldı ve "Merhaba" değeri atandı.

myMessage = myMessage + "Dünya. ";


Opsss.. myMessage objesi tekrar yaratıldı. myMessage'ın ilk tanımlı değeri ile "Dünya" birleştirilerek myMessage'e atanmıştır. myMessage ikinci kere tanımlandı.

myMessage += "Küresel ısınma dünyanın sonu mu?"; // += ile ekleniyor.


Opsss.. Aynı şekilde myMessage tekrar tanımlandı.

.....

Sonuç olarak her yeni atamada tekrar tekrar yeni bir obje yaratılmaktadır. Ne kadar çok obje yaratılıyorsa o kadar çok kaynak tüketimi gerçekleşir. Bu yöntemin handikapı buradan kaynaklanmaktadır. Küçük çaplı stringleri birleştirirken (ki yine de önermiyorum) bu yöntem nispeten kabul edilebilirken, büyük çaplı string birleştirmelerde asla ama asla kullanılmamalıdır.

2. System.Text.StringBuilder sınıfı ile birleştirme

StringBuilder sınıfı string işlemleri için iyi derecede optimize edilmiş çok kullanışlı bir sınıftır. Bu sınıfı kullanarak oldukça hızlı string manipülasyonları gerçekleştirebiliriz.

Kullanımı şu şekildedir :

using System.Text;
.....
StringBuilder sb = new StringBuilder();
sb.Append("Merhaba ");
sb.Append("Dünya.");
sb.Append("Küresel ısınma dünyanın sonu mu?");
sb.Append("Uydurma metin...");

string myMessage = sb.ToString();


Kullanımı çok basit olan StringBuilder sınıfının ToString metodu ile birleştirilmiş metne erişebilirsiniz.

Bu yöntem çok sayıda string'in birleştirilmesi esnasında kullanılırsa büyük performans sağlar. Genel teori açısından 2 veya daha fazla birleştirme işlemi yapacaksanız önerim bu sınıfı kullanmanız.

3. System.Collections.Generic.List Generic sınıfı ile birleştirme

Generics kavramına ucundan bulaşmamışsanız bu yöntemi boşverin. (Ben bilmiyorum ama kasıcam öğrenicem diyen azimli gençleri şuraya alalım. Azminizi takdir ettiğimi de ayrıca belirtmek isterim.)

Yönteme geçmeden önce .Net framework 2.0 ile gelen özelliklerden birisi olan Generics'e biraz değinmek istiyorum. Generics kod kullanılabilirliğinin arttırılması, performans iyileştirmeleri, koleksiyon sınıfları (collection classes) oluşturulması gibi özellikler sunan bir yapıdır. Teorik olarak anlaşılması zor olmakla birlikte inceleyeceğiniz örnek kodlarla yapıya daha aşina olacağınızı düşünüyorum.

Konumuza dönelim ve string birleştirmelerinin List Generic sınıfı ile nasıl yapıldığını görelim.

using System.Collections.Generic;
........
List<string> list = new List<string>();
list.Add("Merhaba ");
list.Add("Dünya. ");
list.Add("Küresel ısınma dünyanın sonu mu?");
list.Add("Uydurma metin...");

string myMessage = String.Join(String.Empty, list.ToArray());


Örnekte, string tipinde bir generic list oluşturulmuş ve değerler eklenmiştir. ( Aklıma gelmişken System.Collections.Generic.List sınıfının ArrayList sınıfının generic versiyonu olduğunu belirteyim. ) Daha sonra liste değerler array haline dönüştürülerek String.Join metodu ile birleştirilmiştir.

Bu yöntem de oldukça performanslı bir şekilde string birleştirmeye yarar.

Yukarıdaki metotları test etmek için ufak bir uygulama yazdım. Ekran görüntüsü aşağıdaki gibidir :

form1Thumbnail.gif

Arayüz çok basit ve anlaşılır olduğu için ayrıntılara girmiyorum ama uygulamanın test kriterlerini özellikle açıklamak istiyorum.

errorcd.gif
Yazının başında belirttiğim örneği canlandırabilmek için yandaki hata sınıfını yazdım. (ErrorHandler.cs) Şüphesiz bu sınıf gerçek bir hata yakalama sınıfı olmaktan öte test amaçlı yazılmış basit bir sınıftır.

Uygulama kodunu incelerseniz property'lerin varsayılan değerleri olduğunu göreceksiniz. Bunu sadece kolay test verisi üretmek adına yaptım.

Yöntemi test etmek için ilgili butona tıkladığınızda yeni bir ErrorHandler objesi yaratılmakta ve o yöntemi kullanarak ErrorHandler property'leri birleştirilerek tek bir string oluşturulmaktadır. (log dosyası yazılabilir, email atılabilir vs. ) Bu işlem yöntemlerin performans farkı insani açıdan fark edilebilir sürelerde olması için 1000 kere tekrarlanmaktadır.





Yaptığım bir test sonucunu göstermek istiyorum :


test1Thumbnail.gif












Sonuçlar şaka gibi değil mi? Dediğim gibi siz siz olun ilk yöntemi kullanmayın. İkinci ve üçüncü yöntemlerin süreleri çoğu zaman birbirine yakınken ilk yöntem onların yanına asla yaklaşamamaktadır.


Test uygulamasını buradan indirebilirsiniz.

"Hızlı" kalın.

Eklenme Zamanı6/28/2007 3:18 AM   Yorum EkleYorumlar (5)   EtiketlerEtiketler : c# , collections , generics , string , stringbuilder

Yorumlar 11/17/2009 11:53 PM -

makaleyi okurken kafamda şöyle bir soru işareti(?) oldu. private static string MyMethod( ilgili parametreler) { return @"Sayın: " + param1 + " biraz uzunca bir yazı " + " devamı " + ........... } şeklinde bir kullanım, yani method içinde parametrelerden başka hiçbir tip tanımlamadan kullanmak performansa ne gibi bir etki edebilir.

Yorumlar 4/18/2008 10:07 PM - Sinan Tavilğolu

Cevabınız için teşekkür ederim. 1000 tekrar konusu gözümden kaçmış kusura bakmayın. Bu arada bizzat kendim test ettim ve bir stringBuilder oluşturmanın çok + yöntemiyle stringleri birleştirmekten çok daha fazla maliyetli olduğu durumları gözlemledim. İyi çalışmalar.

Yorumlar 2/9/2008 10:18 PM - Cihan Uçar

Sinan Taviloğlu, Örnek uygulamadaki test sonuçları 1000 iterasyondan sonra oluşturulmuştur. Yazı içerisinde bunu şu şekilde belirtmiştim: " Bu işlem yöntemlerin performans farkı insani açıdan fark edilebilir sürelerde olması için 1000 kere tekrarlanmaktadır..." Sanırım gözünüzden kaçmış. :) StringBuilder, her şekilde ve her durumda string işlemlerinde daha performanslı çalışmaktadır. Daha performanslı olmasının nedenlerini yukarıda açıkladım. Eğer ki iki küçük string değişkenini birleştirecekseniz "+" yöntem kabul edilebilir; fakat diğer işlemleriniz için bunu kullanmanız performans kaybına yol açacaktır. "Aradaki fark önemsenmeyecek kadar küçük olacaktır." derseniz bunun uygulamanıza göre değişeceğini ve kalitenin en temel ölçütünün hız olduğunu hatırlatmak isterim.

Yorumlar 2/6/2008 2:28 PM - Sinan Taviloğlu

Yukarıdaki sonuçlar kaç iterasyondan sonra elde ettiğinizi belirtmemişsiniz (Kaynak kodu incelemedim). Çünkü benim hatırladığım kadarıyla 10ms'nin altında değerleri alamayacağınız. Örnek kodun çalıştırılması da o kadar sürmeyeceğine göre iterasyonla bu işlemleri birden çok kez tekrarlamış olmanızdır. Bu noktada, siz tavsiye etmesenizde (+) ile işlem yapmak çoğu kez stringbuilder kullanmaktan daha performanslı olacaktır(yukarıdaki senaryoda, bir sql query birleştirmede vs. ) diye düşünüyorum.

Yorumlar 9/23/2007 6:04 PM - ic3b3rg

StringBuilder kullanan ikinci yöntemin birinciden daha hızlı olmasının sebebi birinci yöntemde her seferinde yeni bir string class ı oluşturularak işlemlerin yapılması. Fakat 3. yöntemin birinciden hızlı olmasının sebebi nedir ?

Yorum Ekle

* Yorumlarınız onaylandıktan sonra yayınlanacaktır.
* Ip adresiniz güvenlik gerekçesiyle kaydedilmektedir.