Herkese merhaba,
Bu yazımda C#’ta string birleştirme işlemi yaparken oluşabilecek performans problemlerine ne gibi çözümler üretebileceğimizi, .ToString() ile Convert.ToString() methodları arasında ne gibi bir fark olduğunu ve iki stringi karşılaştırırken kullandığımız .Equals() methodu hakkında ufak bir detayı inceleyeceğiz.
“String birleştirme işlemleri neden maliyetli?” sorusunun cevabı aslında stringlerin .NET Framework’teki tanımlanmalarından ileri gelmektedir. String tipi .NET Framework’te immutable (değişmez) olarak tanımlanmıştır. İlk olarak immutable kavramı bilmeyenler ve yeniden hatırlamak isteyenler için nedir, ne değildir inceleyip; yavaş yavaş string işlemlerinin performanslarına doğru yol alalım isterim.
İmmutable (Değişmez) Nedir?
Immutable nesneler bir kez oluşturulduktan sonra içeriği değiştirilemeyen nesnelere verilen bir tanımlamadır. String tipi de immutable tanımlandığı için, stringler üzerinde yapılan birleştirme işleminde her seferinde memory’de yeni bir string oluşturulacak ve geliştirdiğiniz programın performans sorunuyla karşılaşması muhtemel bir hal alacaktır.
Örneğin, yukarıdaki görselde str isimli bir string oluşturulurken başlangıç değeri olarak “Hello” atanmıştır. Sonrasında str ile “world” birleştirme işlemine tabi tutulup yine str değişkenine atansa dahi memory’de “Hello world” isimli yeni bir String objesi oluşturulmuş ve str artık “Hello world”ü referans göstermektedir. str tanımlanırken verilen “Hello” değeri, artık referans gösterilmeyen bir String objesi olarak memoryde hala yer tutmaya devam etmektedir.
String Birleştirme İşlemleri (StringBuilder Kullanımı)
1 | s = "Hello" + "World" + "I am" "an" + "engineer"; |
Yukarıdaki gibi bir birleştirme işlemi her + operatörünün kullanımıyla memoryde yeni bir string oluşturacağından maliyetli bir işlem olacaktır. Özellikle bir döngü içerisinde bu birleştirme işlemi devam ettiği sürece performansta o oranda bir kayıp yaşanacak ve memory’i gereksiz yere işgal etmiş oluruz. Bunun yerine string’ler ile birleştirme yapacağımız zaman StringBuilder sınıfından yararlanmak performans açısından kaydadeğer bir ilerleme sağlar.
Yukarıdaki birleştirme işlemini StringBuilder ile aşağıdaki şekilde yapabiliriz.
1 2 3 4 5 6 | StringBuilder sb = new StringBuilder("Hello"); sb.Append("World"); sb.Append("I am"); sb.Append("an"); sb.Append("engineer"); string s = sb.ToString(); |
Son satırda StringBuilder objesini ToString() ile string’e çevirerek memory’de yalnızca bir defa string oluşturmuş oluruz. Bir döngü içerisinde stringlerle ilgili birleştirme işlemi yapılacaksa StringBuilder kullanımı oldukça önem kazanıyor, unutulmaması adına bir kez daha belirtmiş olayım.
String Bölme İşlemleri (Split Kullanımı ile İlgili Ufak Bir İpucu)
Elimizde belli bir karaktere göre ayırmamız gereken bir string olduğunda sıkça kullandığımız bir methoddur Split() methodu. Genellikle aşağıdaki şekilde kullanılır;
1 2 | string str = "Ceyhun-Cozvelioglu"; string[] temp1 = str.Split('-'); |
Split işlemini aşağıdaki şekilde uyguladığımızda biraz daha performanslı bir sonuç elde ederiz;
1 2 | string str = "Ceyhun-Cozvelioglu"; string[] temp = str.Split(new char[] { '-' }); |
ÖNEMLİ: Bu yöntem anlamlı bir performans kazancı sağlamamaktadır. Hatta string içerisindeki ayırıcı karakter sayısı arttıkça birbirine yakın sonuçlar elde edilmektedir. Yine de yazıya konu etmeden geçmek istemediğim için bir köşede bulunması amacıyla ele almış bulundum. Performans bazen çok kritik bir önem teşkil etse de büyük ve kalabalık ekiplerin üzerinde çalıştığı projelerde kodun okunabilir olması yerine göre çok daha fazla önem arz ediyor. Bu durumlarda anlamlı bir performans kazancı olmadığı sürece böyle bir yöntemin kullanılması kod okunabilirliğini olumsuz etkileyebileceğinden seçim yaparken bu faktörlerin de göz ardı edilmemesi gerekiyor.
String Equals ile İlgili Ufak Bir Detay
İki string tipindeki veriyi karşılaştırmak istediğimizde, imdadımıza yetişen bir methoddur Equals methodu. Aşağıdaki gibi bir kullanımda true return edecek ve if bloğunun içerisine girerek akıştaki kodumuzu işletecektir.
1 2 3 4 | string str = "Ceyhun"; if(str.Equals("Ceyhun")) ... |
Peki ya veritabanından veya servisten çekeceğimiz bir string değeri bir başka değerle kıyaslayacağımız zaman null gelmesi durumunda Equals methodumuz nasıl bir davranış gösterir?
1 2 3 4 5 6 7 8 9 10 11 | try { string str = null; if (str.Equals("Ceyhun")) { Console.WriteLine("if"); } else { Console.WriteLine("else"); } } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.ReadLine(); |
str değişkenimiz null olduğu için exception fırlatılacak ve programımız crash olacaktır. Bu sebeple karşılaştırma için kullanacağımız string değişkenini null kontrolünden sonra kullanmamız daha güvenli olacaktır.
Peki aşağıdaki gibi bir durumda nasıl bir çıktıyla karşılaşırız?
1 2 3 4 5 6 7 8 9 10 11 | try { string str = null; if ("Ceyhun".Equals(str)) { Console.WriteLine("if"); } else { Console.WriteLine("else"); } } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.ReadLine(); |
Equals metodunu kullanacağımız string null değilse eğer içerisine argüman olarak gönderdiğimiz string değer null olsa bile karşılaştırma işlemi sorunsuz bir şekilde yapılacaktır.
Özetle, string bir referans type olduğu için null değer alabilir. Bu sebeple String sınıfına ait bir methodu kullanmadan önce, string için bir null kontrolü yapmak ve null değilse işleme devam etmek programın stabilitesi açısından faydalı olacaktır.
Yukarıdaki duruma bir örnek de .ToString() methodu ile Convert.ToString() methodu arasındaki farkı verebiliriz.
.ToString() ile Convert.ToString() Arasındaki Fark
İki method da string’e dönüştürme işlemi için kullanılır ancak aralarında ufak bir fark mevcuttur. Eğer dönüşüm yapılacak obje değeri null ise .ToString() ile yapılan dönüştürme işleminde NULL Reference Exception fırlatılırken, Convert.ToString() ile yapılan dönüşümde exception fırlatılmaz.
1 2 3 4 5 6 7 | //Null reference exception fırlatılacaktır. object obj = null; string str = obj.ToString(); //str stringinin değeri null olacaktır. Exception fırlatılmaz. object obj = null; string str = Convert.ToString(obj); |
Özet Olarak
- Stringler .NET Framework içerisinde immutable olarak tanımlanmıştır.
- Stringler ile yapılan birleştirme işlemlerinde StringBuilder kullanımı performans açısından önemlidir.
- Stringlerin NULL kontrolü yapıldıktan sonra işlemlere tabi tutulması güzel bir kazanım olacaktır.
- Bir objeyi stringe dönüştürmek için Convert.ToString() methodunu kullanmak best practice’tir.
Bu yazımda stringler hakkında bazı ufak ancak gözden kaçtığı taktirde can sıkabilecek detayları inceledik. String’leri benzine benzetiyorum zaman zaman, herkesin kullanmak durumunda olduğu ancak maliyeti yüksek bir yapı, neyse ki yazılımda maliyeti düşürmenin yolları mevcut 🙂
Herkese iyi günler, keyifli kodlamalar dilerim :)!