C# 8.0’dan Sonra Kodlamada Yeniliğe Gittiğim Özellikler

Selamlar,

Bu makalede .Net 8.0 ile gelen bir takım yeni özelliklerden ve .Net 6.0’dan sonra kullanmaya başladığım kod yapılarından bahsedeceğim.

Tüm denemeler Visual Studio 2022 Version: 1.6.0 Preview 1.0 ile yapılmıştır.

Ayrıca yeni bir Console Application yaratılıp, Proje çift tıklanarak alttaki gibi “<TargetFramework>net8.0</TargetFramework>” şeklinde değişikliğe gidilmiştir.

1-) Default Interface Methods:

Benim en çok dikkatimi çeken yeniliklerden biri de C# 8.0 ile gelen, Interface içine dolu default method yazabilme özelliğidir. Amaç Interface içinde yazılan methodların, inherit edilen sınıf içinde implemente edilmeden kullanılabilmesidir. Kısaca aslında bu yenilik, inheritance kavramına bambaşka bir boyut katmıştır. Design patternler için artık kartlar yeniden dağtılacaktır.

Aşağıdaki örnekde, Interface içini yazılan “AnalizeFile()” default methodu ile, bir mail’in body’si içindeki tüm linkler parse edilip console’a basılacaktır. Kısaca sınıfa dokunulmadan, istenen yeni özellik “AnlizeClass” sınıfına kazandırılmıştır.

Yukarıdaki örneği aşağıdaki gibi değiştirdiğimizde, IAnalize interface’ine eklenen “GenrateFileName()” methodu boş olduğu için ve AnalizeClass sınıfında implemente edilmediği için, hata fırlatılmaktadır.

Şimdi Gelin Interface Segregation’ı Bir Düşünelim:

Aşağıdaki örnekde IMobile interface’i, IService interface’inden türemiştir. Aslında bir class farklı görevlere ait 2 class’a ayrılırken, Interface olarak Web ve Mobile olarak ayrıştırılmıştır. IMobileService, IService’in tüm özelliklerine sahiptir. Şimdi biz IService’e, default method atarsak ne olur ? Mesela aşağıdaki örnekde:

  • IService interface’ine “CheckToken()” ve “GetUserId()” default methodları tanımlanmıştır.
  • IMobileService interface’inde, “CheckToken()” default methodu tekrar override edilmiştir.
  • MobileService’i sınıfı, IMobileService’den türetilmiştir.

Program.cs/Main(): Aşağıdaki MobileService sınıfı, IMobileService’den türemiştir. Şimdi biz bunun “CheckToken()” methodunu çağırdığımızda, nasıl bir sonuç ile karşılaşacağız. Gerçekten kod okunaklığı şu anda, maalesef kaybolmuş durumdadır. Gelin hep beraber inceleyelim.

  • Öncelikle MobileService sınıfı “CheckToken()” methodunu implemente etmemiştir.
  • IService interface’i “CheckToken()” methodunu, default method olarak tanımlamıştır.
  • Son olarak IMobileService interface’i de “CheckToken()” default methodunu override etmiştir. Demek ki son olarak, IMobileServisine ait olan “CheckToken()” methodu çalışacak ve “true” değeri geriye dönülecektir.

Default Interface Methodu’nun esas amacı, eski kodları değiştirmeden sınıflara yeni özelliklerin getirilebilmesidir. Örneğin, ortak “IAnalize” interface’den türemiş “UrlAnalize” ve “AttachmentAnalize” sınıflarımız olsun. Bu sınıfları bozmadan sisteme yeni dahil olan yine “IAnalize“‘dan türemiş “ImageAnalize” sınıfımızın, “CheckSize()” adında yeni bir methoda ihtiyacı olsun. Eski yol ile, bu sınıf için yeni bir Interface tanımlayıp (“IImageAnalize“) “ImageAnalize“‘ı hem bu yeni “IImageAnalize” Interfaceinden hem de “IAnalize“‘dan türetebilirz. [ImageAnalize : IAnalize, IImageAnalize] Ama var olan sınıfların da, bu “CheckSize()” methodunu kullanmasını ister isek, ya tek tek hepsine bu methodu tanımlayacağız, ya da “IAnalize” interface’ine Default Method olarak “CheckSize()” methodunu ekleyeceğiz. Böylece, önceden yazılmış kodlarda hiçbir değişikliğe gitmeden, ilgili eklentiyi yapılmış olacağız.

2-) Readonly Struct:

Aşağıdaki örnekte Struct içinde atanan değerler, sadece ilk sefer Constructor’da atanmakda ve bir daha değiştirilememektedirler. Böylece Immutable Struct oluşturulmuştur. Burada amaç, başta atanan propertylerin bir daha değiştirilmeden, sonuçlarının farklı zamanlarda tekrardan alınmasıdır.

Aşağıda Rectangle Struct’ının Height ve Width propertyleri ilk ayağa kaldırılken constructor’da set edilmiş, ve daha sonra istenen alan bilgisi çekilmiştir. Aynı alan bilgisi, projenin başka yerlerinde de değişmeden kullanılabilecektir.

3-) Using Declarations:

Aşağıdaki örnekde “using” süslü parantezler olmadan basit bir şekilde kullanılmıştır.=> “using StreamReader file = new StreamReader(filePath)“. Amaç okunabilirliği arttırmaktır.

Alttaki “WriteLinesFromFile()” methodu ile, belirtilen “.txt” bir dosya, satır satır Console’a basılmaktadır.

4-) Static Local Functions Ve Switch Expression :

C# 7.0 ile gelen Local functions, C# 8.0 ile artık Static olarak da tanımlamabilmektedir. Aşağıdaki örnekde, girilen geometri tipine ve boyutlara göre, alanı hesaplanmaktadır. Ayrıca Switch Expression, bence bugüne kadarki olabilecek en sade ve anlaşılır kodu bize sunmaktadır.

  • ShapeTypes Enum ile, belirli Geometrik şekillerin alanı hesaplanmaktadır.
  • CalculateShape() static methodu içinde => “static void CalculateArea(ShapeTypes type, int Num1, int Num2)” methodu local olarak tanımlanmıştır. Böylece bu local function içinde tanımlı hiçbir parametreye, dışardan erişilememektedir.
  • var area = type switch {“: C# 8.0 ile gelen Switch Expression ile, kod okunaklığı arttırılmış ve sadeleştirilmiştir.
  • ShapeTypes.Kare => Num1 * Num2“: İlgili Geometrik şeklin Kare olması durumundaki alan hesabı, sade bir şekilde tanımlanmıştır. Benzer işlemler, “Daire” ve “Ucgen” için de yapılmıştır.
  • “_ => 0”: Bilinmeyen bir şekil parametrik olarak verildiğinde, bu default seçeneğine gelinmekte ve alan için “0” değeri atanmaktadır.

Sonuç:

5-) Asynchronous Streams:

Artık “foreach loopları async tanımlaması ile, asenkron olarak çağıra bilmekteyiz. Aşağıdaki methodda ilk göze çarpan, geri dönüş tipi “IAsyncEnumerable<int>“‘dir. Ayrıca “yield return” keyword’ü ile her üretilen değer, asenkron olarak loop’dan çıkılmadan teker teker geri dönülmektedir. Her bir loop arasına “await Task.Delay(100)” 100ms bekleme konulmuştur.

Şimdi sıra geldi bu methodu bir loop içinde asenkron olarak console’a basmaya: Aşağıdaki kodda, en önemli kısım “await foreach()” kullanım şeklidir. Böylece Stream şeklinde gelen akış, console’a asenkron olarak basılmaktadır.

6-) Tanımlı Olmayan Tipler İçin Raw SQL Queryler .NET 8.0:

.Net 8.0 ile hayatımıza girecek olan ve bence geç kalınmış bir Entity özelliğidir. Artık Dapper’a hiç ihtiyaç kalmayacaktır. Öncelikle DAL Class Library projesi yaratılır. Sonra aşağıdaki kütüphaneler Nuget’den indirilir.

Aşağıdaki komut ile Test DB’sinden TestContext ve User ile UserAddress Entityleri, DBFirst ile aşağıdaki scaffod komutu sayesinde otomatik oluşturulur.

MsSql TestDB yapısı, aşağıdaki gibidir:

UserData: Custom query sonucu olarak geri dönülecek DB’de karşılığı olmayan, “CustomDataModel” aşağıdaki gibidir.

Program.cs: Aşağıda görüldüğü gibi ilk önce TestContext yaratılmıştır.

  • var services = new ServiceCollection()“: Servis collection oluşturulur.
  • var serviceProvider = services.BuildServiceProvider()“: Burada Dependency Injection kullanılmamıştır.
  • services.AddDbContext<TestContext>“: Burada TestContext database Connection string ile oluşturulmuştur. İlgili connection’ın, profesyonel hayatta configden encrypted olarak alınması gerekir :)
  • “var serviceProvider = services.BuildServiceProvider()”: Service Provider oluşturulmuştur.
  • “_testDbContext = serviceProvider.GetService<TestContext>()”: TestDBContext ayağı kaldırılır.
  • await _testDbContext.Database.SqlQuery<UserData>()“: UserData, User ve Address entitylerinin join’i ile oluşturulmuş DB’de karşılığı olmayan DataView modeldir. Yukarıda, ilgili Model’in propertyleri tanımlanmıştır. Yazılan RawSql sonucu, asenkron olarak beklenmektedir.
    • select US.Id as UserID, us.Name,us.Surname, ua.Id as AddressID, ua.Address,us.IsDeleted from [dbo].[User] as us left join [dbo].[UserAddres] as ua on us.Id =ua .UserId“: Bu query’de, Userların tamamı ve varsa user Adres bilgisi left join ile alınmıştır.
  • “.Where(u => u.IsDeleted == false) .ToListAsync()”: Where koşulu ve result’ın ListAsync olarak dönmesi sağlanmıştır.

Sonuç:

Aslında RawSql, dinamik Query ihtiyacında, tam bir can simidi olmuştur. Örneğin paramterik gelen Tablo veya DB isimlerinin, Linq Query ile yazılması pek mümkün değildir. Bu durumda RawSqller devreye girer. .Net Core 2.0’a kadar Anonymous tipde RawSql yazmak desteklenirken .Net Core 3.1 itibari ile bu destek ortadan kaldırılmıştır. Taa ki çıkıcak olan .Net 8.0’a kadar. Sanırım Linq Query haricinde, RawSql halen eski populerliğini korumakta ve DB karşılığı olmayan, birçok tablo ile Joinli olan sorgu sonuçları artık Entity 8.0 ile desteklenmektedir. .Net 7.0 ve öncesi için Custom çözümleri bu makaleden erişebilirsiniz.

Daha bahsetmediğim ve kullandığım birçok yenilik bulunmaktadır. Ama makalenin de bir sınırı olduğundan, 6.Maddede kesmek zorunda kaldım. Belki birkaç farklı makale ile diğer özelliklerden de ilerde bahsederim. Geldik bir makalenin daha sonuna. Yeni bir makalede görüşmek üzere hepinize hoşçakalın.

Kaynaklar:

Herkes Görsün:

Bunlar da hoşunuza gidebilir...

4 Cevaplar

  1. Necip Baran dedi ki:

    Emeğine sağlık Bora hocam

  2. Ali Kara dedi ki:

    Hocam Başlık Olarak c# 8 kullanmışsınız c# 12 ve .net 8 olmayacakmı ?, anlayan anlamıştır tabikide yinede düzeltmekte fayda var diye düşünüyorum.

  3. emre dedi ki:

    Merhaba bora hocam default interface methods o kadar saçma geldi ki anlatamam. Interface amacı contracttır ama bu srp ayrı bir halde işte yapıyor. Tek görevi contract tutmak olmalı. Eee interface ile abstract classın ne farkı var şimdi? Bu tarz problemler hep abstract class üzerinden çözülmeliydi.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.