C# Best Practices – En İyi Pratikler Nelerdir?

Kodlama yaparken en iyi yöntemin ne olduğu konusunda sürekli bir muhabbet, bir tarışma konusu oluşur. Herkesin bilgi birikimine, yaşamış olduğu tecrübelere göre en iyi yöntem, her zaman kesin olarak belli olmayacağı gibi benimde yaşadığım tecrübe ve bilgi birikimime göre C# kodlaması sırasında uyulması gereken pratikleri tek bir makale altında paylaşmaya çalıştım ve güncel olarak paylaşmaya çalışacağım.

C# Best Practices

1) Bir method sadece bir sorumluluğu yerine getirmelidir

Bu kuralı anlatacak en iyi görsellerden biri herhalde şu olacaktır.

Bu maddede aslında dikkat etmemiz gereken asıl nokta, method özünde Single Responsibility prensibine özen göstermektir. Bu sayede bize kazandıracağı bazı avantajlar ise:

  • Method’un complexity seviyesini azaltmak
  • Hataları azaltıp, tekrar kullanılabilirliği sağlamak
  • Okunabilirliği ve genişletilebilirliği sağlamak
  • Daha tutarlı testler yazılabilir hale getirmek

gibi maddeler olacaktır. Şimdi bir örnek üzerinden giderek mevcut olanı ve olması gerekene bir bakalım.

Tamamen örnek amaçlı olan şifre değiştirmeye yarayan bir method düşünelim. İçerisine baktığımızda ise context üzerinden user’ı çekip, ilgili kontrollerden geçirdikten sonra şifre değiştirme işlemini gerçekleştiriyor ve hemen ardından şifre değişikliği üzerine bir mail gönderiyor. Gördüğümüz gibi buradaki method complexity’si artmış ve en önemlisi mail gönderim kısmının tekrar kullanılabilirliği kısıtlanmış durumdadır.

Optimal bir şekilde refactor etmek gerekirse:

Gördüğümüz gibi her method sadece ilgili sorumluluğunu yerine getirmektedir. SendEmail method’uda artık tekrar kullanılabilir bir hale gelmiştir.

2) Method’lar inline olarak çok fazla parametre almamalıdır

İsminden de anlaşılabildiği gibi bir method çok fazla parametre almamalıdır. Örneğin:

Bu tarz kullanımlarda farklı bir business kararı gereği değişmesi gereken veya ekstradan eklenmesi gereken bir parametre daha gerekebilir. İşte bu durumda bu method’u kullanan her yerde bu değişimleri yapmak zorunda kalabiliriz ve parametre sıralarının kayması gibi problemleri de göze almalıyız. Bunlara ek olarak da okunabilirliği azaltıyor. Çözümü ise bu parametreleri bir obje içerisinde encapsulate etmektir.

Eğer ideal parametre sayısını merak ediyorsanız “Clean Code: A Handbook of Agile Software Craftsmanship” kitabında geçen çok güzel bir dizeyi size göstermek isterim:

The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification—and then shouldn’t be used anyway.

3) Satırlar çok fazla uzun olmamalıdır

Kod yazarken uzun uzaya giden satırlar, belli bir süre sonunda tek hamlede okunabilirliği yüksek ölçüde azaltmaktadır. Buda ilgili kodu anlamamızı veya bazı kontrol etmemiz gereken durumları gözden kaçırmamıza sebebiyet verebilir. Çözüm olarak ilgili kodun belirli parçalarını, sub method’lar halinde mantıklı bir şekilde bölmektir. Farklı kaynaklarda tavsiye edilen satır uzunlukları 200’dür.

4) Exception’lar es geçilmemelidir

Kodlamanın herhangi bir parçasında catch bloğu içerisinde es geçilen exception’lar, ilerleyen süreçlerde farklı hata ve problemlere sebebiyet verecektir. Aynı zamanda da iyi bir best practice değildir. Bu tarz durumlarda oluşan exception’ı loglamak, iyi bir yöntem olacaktır.

5) Switch-Case clause’ları çok fazla satır içermemelidir

Switch-Case clause’ları içerisindeki fazla satırlar, okunabilirliği ciddi ölçüde azaltmaktadır. Bu anlamda olabildiğince kısa statement’lar kullanılmaya çalışılmalıdır.

Burada method çağırımının haricinde satırlarca farklı kodlar da yer alabilirdi. Okunabilirlik için olabildiğince case clause’u içerisindeki statement’ları kısaltmaya çalıştık.

6) Boolean statement’leri ters kontrol edilmemelidir

Boolean kontrollerini beklenenin aksine kontrol etmek ve ters ifadeler ile kontrol etmek hem coding standartlarına uygun değildir hem de kompleks ve maliyetli bir iştir.

7) TODO tag’ları kontrol edilmeli ve method’lara summary eklenmelidir

Eğer herhangi bir method boş ise ve daha sonra implemente edilecekse, TODO tag’ı eklenmelidir. Bu sayede TODO tag’larına göre filtrelendiğinde gözünüzden kaçan implemente edilmemiş kod parçacığı kalmayacaktır. Bunun yanı sıra method’lara eklemiş olduğunuz summary’ler sayesinde, hem siz hemde diğer ekip arkadaşlarınız kod’a henüz bakmadan summary ile kod hakkında bir fikir sahibi olabilirler.

8) Switch-Case statement’ındaki boş DEFAULT clause’u silinmelidir

Switch-Case kullanıldığında otomatik olarak gelen DEFAULT clause’u kullanılıyorsa, o halde olması gereken uygun action kullanılmalıdır. Eğer hiç bir şey kullanılmayıp boş olarak es geçiliyorsa, kullanmanın da bir anlamı olmayacağından dolayı coding standartlarına göre silinmelidir.

9) Enum tanımlarken null olarak sıfırıncı değer tanımlanmalıdır

Coding standartlarında tutarlılığı sağlayabilmek adına Enum’lar tanımlanırken, sıfırıncı değer None olarak tanımlanmalıdır.

10) Constructor aracılığı ile inject edilen field’lar, read-only olmalıdır

Constructor aracılığı ile inject ettiğimiz field’ları read-only olarak tanımlamazsak, farklı method’lar içerisinden de ilgili field’a değer atanmaya çalışılabilir, karışıklıklara sebebiyet verebilir. Bunların önüne geçebilmek için ise read-only olarak işaretlenmelidir.

11) Interface tanımlanırken başına “I” prefix’i getirilmelidir

Coding standartlarına göre interface’ler tanımlanırken başlarına “I” prefix’i eklenmelidir. Örneğin:

12) String birleştirme işlemlerinde “+” öperatörü kullanımına dikkat edilmelidir

String birleştirme işlemlerinde sadece bir kaç string’i birleştiriyor isek çok fazla problem olmayacaktır. Fakat bu işlemi daha fazla string ile gerçekleştiriyor isek memory performansı açısından StringBuilder veya String.Concat gibi işlemler uygulanmalıdır.

13) System type name’ler yerine Predefined type name’ler kullanılmalıdır

Int16, Single, UInt64 gibi system type name’ler yerine, Predefined type name’ler kullanılmalıdır. Örneğin:

14) External kaynaklara erişecek bir class varsa IDisposable pattern’ini implemente edilmelidir

IO işlemleri, web servisler, sql gibi external kaynaklara ihtiyaç duyduğunuz durumlar olduğunda, memory performansı için IDisposable pattern’ini implemente edin.

15) Kodlama yaparken kodlar içerisinde magic string/numbers kullanılmamalıdır

İlgili kod parçacıkları içerisinde kullanılan magic string/number’lar tekrar kullanılabilirliği engellemekle kalmayıp, ilgili değer değiştiğinde ise her nerede kullanıldıysa tek tek bulunup güncellenmesi gerekmektedir. Coding standartları doğrultusunda bu tarz değişkenler global ve const olarak tanımlanmalıdır.

16) Unmanaged kaynaklar için using statement’ı kullanılmalıdır

Bildiğimiz gibi .Net Framework içerisindeki Garbage collector hiçbir referans tarafından gösterilmeyen managed nesneleri bellekten kaldırmaktadır. Ancak Garbage collector unmanaged kodlar üzerinde tam kontrole sahip değildir. Kullanılmıyor olsalar bile bellekten serbest bırakamaz. İşte bu noktada biz yazılımcılar tarafından Dispose edilmesi gerekmektedir. Bu tarz unmanaged kaynaklara erişen objeler genelde IDisposable arayüzünü implemente etmektedir. Bu nesneleri dispose edebilmenin en iyi yolu ise, using statement’ı içerisinde kullanmaktır.

17) Catch bloğunda exception tekrardan fırlatılırken sadece throw kullanılmalıdır

Catch bloğu içerisinde ilgili exception’ı logladıktan sonra tekrardan geriye fırlatma ihtiyacı duyulursa, bunu sadece throw keyword’ü kullanılarak gerçekleştirilmelidir. Aksi durumda oluşan orjinal exception’ın stack trace’i kaybolacaktır.

18) İsimlendirme Kuralları ve Standartları

C#’da genellikle 2 farklı isimlendirme standartı kullanılır bu isimlendirme standartları ise şöyledir;

  • Pascal Case
  • Camel Case

Bu isimlendirme standartlarını kısaca özetlemek gerekirse PascalCase tüm kelimelerin baş harfleri büyük olacak şekilde yazıma sahip olan bir isimlendirme standartıdır. CamelCase ise ilk kelimenin baş harfi ve sonraki kelimelerin baş harfi büyük olacak şekilde yazıma sahip bir standarttır.

Örnek verecek olursak PascalCase için MuratOner yazımı doğrudur ama CamelCase için muratOner yazımı doğrudur.

Peki bu 2 isimlendirme standartını C#’da nerede kullanacağız?

Sınıf, Metod ve Property isimlerinde PascalCase kullanılır.

Gördüğünüz üzere PascalCase adındaki sınıfımız, MethodName adındaki metod adımız ve Name adındaki Property’imiz PascalCase standartında yazılmıştır.

Class kapsamında tanımlanan private field’lar, metod propertyleri ve metod kapsamında tanımlanan değişkenler CamelCase isimlendirme standart’ına uygun şekilde yazılmalıdır. Altta bu konu ile alakalı örneğe gözatabilirsiniz.

19) Namespace’leri Using Bloklarında Kullanın

Namespaceleri using bloğunda kullanmadığımız sürece namepspace’ler altındaki her nesneye erişimde uzun kod satırları oluşabiliyor örnek olarak UTF-8 encoding türünü kullanmak için System.Text namespace’i altında yer alan Encoding sınıfının statik bir üyesi olan UTF8 özelliği üzerinden ulaşabilecğiz ama namespace’i using bloğunda kullanmadığımızda şu şekilde bir kod çıkacak ortaya.

Ama kullanılması gereken asıl yöntem şu şekilde olmalıdır.

20) Property veya field’e bir örneğinizi atamak istediğiniz new() kodunu kullanın

Daha önce property ve field tanımlarken bir sınıf yada veri kümesinden örnek almak istediğimizde alttaki gibi bir kullanımla ilerliyorduk.

Örnek property ve field için örnek tanımı alttaki gibi kısa sözdizimi ile kullanabilirsiniz.

 

Sizlerle önemli olarak ele aldığım ve alınan, code review’ler sırasında da dikkat ettiğim, farklı kaynaklardan da derlediğim best practice’leri paylaşmaya çalıştım. Umarım herkes için faydalı bir makale olur.


✍ Lütfen olumlu-olumsuz tüm görüşlerinizi bana yorum yada mail yolu ile iletmeyi ihmal etmeyin.

🔗 Sosyal medya kanallarından makaleyi paylaşarak destek olursanız çok sevinirim.

👋 Bir sonraki makalede görüşmek dileğiyle.

5/5 - (1 vote)