Category Archives: C#

C# programlama dili ile ilgili herşey

Switch içerisinde Hash Table optimizasyonu [C#]

C# compiler’ının switch statement’ı içerisinde 7. case’den sonra bir hash table optimizasyonu yaptığını biliyor muydunuz?

C# compiler’ı switch statement‘ını 6 tane case sayısına ulaşana kadar lowering mekanizması ile standart if else ifadelerine çeviriyor.

Fakat case sayısı 7’ye ulaştığında lowering mekanizması akıllıca bir optimizasyona gidiyor. Bu, C# compiler’ın işini zamanında yapmasını sağlayan çok akıllı bir optimizasyon. Bu optimizasyon, aynı string’lerin hash code’larının aynı olması gerektiği gözlemine dayanıyor. Bu yüzden string’leri teker teker eşitliğini kontrol etmek yerine, compiler hedef string’in bir hash’ini hesaplıyor table-lookup mantığında çalışmasını sağlıyor yani O(1) zamanda.

Bu Roslyn’in github sayfasında şöyle bahsedilmiş:

The string switch codegen in Roslyn is completely new. Roslyn does not use dictionaries to avoid allocations and a potentially huge penalty when a string switch is execute for the first time. Roslyn uses a private function that maps strings to hash codes and a numeric switch. In some sense this is a partial inlining of the former technic that used static dictionaries.

Kaynaklar:

https://sharplab.io/#v2:EYLgtghglgdgPgAQEwEYCwAoBBmABM3AYVwG9NcL89YAXXAWQAoEUAGXAZwEpdzKyMlIZwDuUGgGMAFo259hpeQsoSIHAKa4ARBC0h8AdlwoA3EuW5VG7cD2HcSM4Isq1mrRLsIj2Jy9fWWgAmXkYALH7+lm7a6qG4AKyR/lbuAGbxAGzJLqnaAObxBjkWQeppEACuADY0+t64ALSm5pQAvkodGG1AA=

https://en.wikipedia.org/wiki/Switch_statement#Advantages_and_disadvantages

https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs

C# 11 ile Gelen Yenilikler

Microsoft’un yayınladığı blog yazısına istinaden C# 11 ile gelebilecek özelliklere göz attık.

Öncelikle .NET 7 henüz long term support olmadığı için, C# 11’i kullanmak adına bazı ayarlar yapmanız gerekiyor. Bunun için visual studio 2022 17.1 versiyonunu indirmeli (community, professional veya enterprise edition fark etmez) ve .NET SDK’sının 6.0.200 versiyonunu kurmanız gerekiyor. Son olarak ta, .NET 6 ile oluşturduğunuz projenizin csproj dosyasında LangVersion tag’ını preview olarak eklemeniz gerekiyor.

✅ String interpolation’daki yenı satır kısıtı

Bildiğiniz gibi verbatim string literal‘ler içerisinde yeni satırda kullanım sağlayabiliyorduk fakat non-verbatim string literaller içerisinde bunun için \r\n kullanmamız gerekiyordu. C# 11 ile birlikte, non-verbatim interpolated string’ler için de yeni satır kısıtını kaldırmışlar.

var v = $"sayı\t: { 5.tostring()
.ToString(
Cultureınfo.InvariantCulture)}";

✅ List patterns (opsiyonel slice patterns)

Liste ve dizilerde pattern matching desteği geliyor. List pattern’lar köşeli parantezlerle belirtilirken, slice pattern’lar ise iki nokta (..) ile belirtiliyor ve isterseniz var pattern’ı da kullanabiliyorsunuz. İlerleyen zamanlarda IEnumerable tipler için de list pattern desteği getirmeyi planlıyorlarmış.

✅ Parameter null-checking (!! operatörü)

Ve geldik .NET dramasına dönen o yeniliğe. Hem Twitter hem de Github’ta çok fazla tartışıldı bu operatör. Ek olarak  @Sedat Kapanoglu  da bu yeniliği neden beğenmediğini yine aynı thread içerisinde paylaştı.

https://github.com/dotnet/csharplang/discussions/5735#discussioncomment-2153360

Özetle, C#’ta bir argümanın null kontrolü klasik olarak aşağıdaki şekildeydi;

void M1(string s)
{
   if (s is null)
   {
      throw new ArgumentNullException(nameof(s));
   }
}

.NET 6 ile ThrowiIfNull statik metodu geldi.

void M2(string s)
{
   ArgumentNullException.ThrowIfNull(nameof(s));
}

ve C# 11 ile birlikte !! (bang bang) operatörü hayatımıza girecek aynı işlevi gören.

void M3(string s!!)
{
}

Bu arada bu özellik nullable reference types ile birlikte çalışabiliyor. NRT compile time’da, parameter null-checking runtime’da kontrol şeklinde düşünebilirsiniz.

Lambda parametreleri desteği var. Indexer parametreleri için kullanılabilir. Get ve set içerisinde kontrol edilir. Constructor’lar için “field initialization”‘dan önce kontrol ediliyor. async metodlarda da kullanım mevcut.

Kaynak:

https://devblogs.microsoft.com/dotnet/early-peek-at-csharp-11-features/

https://github.com/dotnet/csharplang/discussions/5735

https://sharplab.io/#v2:EYLgtghglgdgPgAQEwEYCwAoBBmABM3AYVwG9NcL8AWXAWRQAoEUAGXAZwEpzKyNKBuKADNcDdkIkwArgBtZ3foIp9lygC4ALAE4B7AO64YAU0MBBbQHNpYYzHUA5ObICiADwDGxgA7qoumAYYCFtdYXFOTgBuHmUAX1jcBIxEgHpU3AA6BxcAFVwANkxEhBpaJCZWDkUBVTULa1t7J3l3L19/GEzcnQMASWEW2SCQ4zCI6MTkkrLsSrZ2AEJFmt4pzDigA=

Minimal API | Kuruluş ve Swagger Kurulumu

Minimal API‘ler, .NET 6 ile birlikte gelen, minimum bağımlılıkla HTTP API’leri oluşturmak için tasarlandı. ASP.NET Core‘da yalnızca minimum dosyaları, özellikleri ve bağımlılıkları dahil etmek isteyen mikro hizmetler ve uygulamalar için idealdirler.

Minimal API için en basitinden 4 satır koda ihtiyacımız var.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();

İşte bu kadar. Elinizde çalışan bir http restful bir api’ınız oldu. Bu geliştirdiğiniz api artık postman üzerinden denemeye hazır.

Swagger nedir peki?

Swagger, JSON kullanılarak ifade edilen RESTful API‘leri açıklamak için bir arayüz tanımlama dilidir. Swagger, RESTful web hizmetlerini tasarlamak, oluşturmak, dökumante etmek ve kullanmak için bir dizi açık kaynaklı yazılım aracıyla birlikte kullanılır. Swagger, otomatik dokümantasyon, kod oluşturma (birçok programlama dilinde) ve test senaryosu oluşturma içerir.

Swagger için kullanılan methodların açıklaması şu şekilde;

app.UseSwagger — ara katman yazılımının oluşturulan OpenAPI açıklamasını JSON içeriği olarak sunmasını sağlar.

app.UseSwaggerUI — Middleware’in Swagger UI öğelerine hizmet etmesini sağlar.

SwaggerEndpoint — OpenAPI açıklamasının endpoint’ini belirtir.

Peki klasik controller ile geliştirilen api’lerden ne farkı var Minimal api’nin?

☑️Filtre desteği yok. AuthorizationFilter, ActionFilter, ExceptionFilter, ResultFilter gibi mesela.

☑️ModelValidator desteği yok.

☑️ModelBinder desteği yok. Custom olarak eklenebilir tabi bu.

✅Github reposu: https://github.com/sonergonul/MinimalAPI

Kaynaklar:

https://docs.microsoft.com/en-us/aspnet/core/tutorials/min-web-api?view=aspnetcore-6.0&tabs=visual-studio

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-6.0

https://medium.com/geekculture/minimal-apis-in-net-6-a-complete-guide-beginners-advanced-fd64f4da07f5

https://dotnetcoretutorials.com/2021/07/16/building-minimal-apis-in-net-6/

https://dev.to/moe23/getting-started-with-minimal-apis-in-net-6-4mi4

BenchmarkDotNet | Baseline ve Jobs 3. Kısım

BenchmarkDotNet içerisinde baseline ve Jobs konularına baktık bu videoda. Bu video benchmarkdotnet içerisinde şu konulara göz attık;

💎 Baseline

[Benchmark(baseline = true)]

public void Sleep200() ==> Thread.Sleep(200);

💎 SimpleJobs

[SimpleJob]

💎 RunStrategy

[SimpleJob(runStrategy: RunStrategy.Throughput)] 

💎 RuntimeMoniker

[SimpleJob(runtimeMoniker: RuntimeMoniker.Net50)] 

💎 Orderer

[Orderer(SummaryOrderPolicy.SlowestToFastest)]

Özellikle RuntimeMoniker kullanırken seçtiğiniz ortamlara ait SDK’ları bilgisayarınıza kurmayı ve projenizin csproj dosyasına hedef ortam bilgisini TargetFrameworks tag’leri arasına yazmayı unutmayın.

Kaynak: https://benchmarkdotnet.org/articles/overview.html

BenchmarkDotNet | Kurulumu ve Temelleri 1. Kısım

Yazdığınız bir kod parçası ne kadar verimli? Az önce yaptığınız değişiklik uygulamanızı iyileştirecek mi? Bunu nereden biliyorsunuz? Bu değişiklik ne kadar önemliydi? Bunlar kıyaslama yani benchmarking yapmadan cevaplanamayacak sorular. Bu video serisinde, kodun ne kadar verimli olduğunu belirlemek için Benchmarkdotnet‘in kodu değerlendirmek için nasıl kullanılacağına ve ardından kodun değiştirilmiş bir versiyonuna kıyasla ne kadar verimli olduğuna bakacağız.

BenchmarkDotNet, yöntemleri karşılaştırma ölçütlerine dönüştürmenize, performanslarını izlemenize ve tekrarlanabilir ölçüm deneylerini paylaşmanıza yardımcı olur. Birim testleri yazmaktan daha zor değil! BenchmarkDotNet sizi popüler kıyaslama hatalarından korur ve kıyaslama tasarımınızda veya elde ettiğiniz ölçümlerde bir sorun olduğunda sizi uyarır. Sonuçlar, denemenizle ilgili tüm önemli gerçekleri vurgulayan kullanıcı dostu bir biçimde sunulur. Proje, .NET Runtime dahil olmak üzere 6800’den fazla proje tarafından benimsenmiş ve .NET Foundation tarafından desteklenmektedir.

Tipik bir durumda ne yapmanız gerektiğini düşünelim. İlk olarak, bir pilot deney yapmalı ve en iyi yöntem çağırma sayısını belirlemelisiniz. Ardından, birkaç ısınma yinelemesi gerçekleştirmeli ve karşılaştırma ölçütünüzün sabit bir duruma ulaştığından emin olmalısınız.

Elle yazılmış birçok kıyaslama, yanlış iş kararlarına yol açan yanlış sayılar üretir. BenchmarkDotNet, sizi kıyaslama tuzaklarının çoğundan korur ve yüksek ölçüm hassasiyeti elde etmenizi sağlıyor.

Mükemmel yöntem çağırma sayısı, ısınma ve gerçek yineleme sayısı hakkında endişelenmemelisiniz: BenchmarkDotNet en iyi kıyaslama parametrelerini seçmeye çalışır ve ölçüm öngörüsü ile tüm kıyaslama çalıştırmalarının toplam süresi arasında iyi bir denge elde etmeye çalışır.

Videoda neler öğrendik?

➡️ Benchmark attribute kullanımı

➡️ MemoryDiagnoser attribute kullanımı

➡️ BenchmarkRunner.Run kullanımı

✅ Gen 0, Gen 1 ve Gen 2 nedir ve nasıl çalışır?

Küçük .NET nesneleri, küçük heap’lere tahsis edilir. Bunlardan üç tane vardır: Gen 0, Gen 1 ve Gen 2. Gen 0’a yeni nesneler yerleştirilir.

Gen 0 dolduğunda, .NET Garbage Collector çalışır, artık ihtiyaç duyulmayan nesneleri elden çıkarır ve diğer her şeyi Gen 1’e taşır. Gen 1 dolarsa, GC yeniden çalışır, aynı zamanda Gen 1’deki nesneleri Gen 2’ye taşır.

Gen 2 dolduğunda tam bir GC çalışması gerçekleşir. Bu, gereksiz Gen 2 nesnelerini temizler, Gen 1 nesnelerini Gen 2’ye taşır, ardından Gen 0 nesnelerini Gen 1’e taşır ve son olarak referans verilmeyen her şeyi temizler.

Kaynaklar:

✔️ https://benchmarkdotnet.org/

✔️ https://www.red-gate.com/products/dotnet-development/ants-memory-profiler/learning-memory-management/memory-management-fundamentals

Hepsine hükmeden tek bir döngü – While

C# compiler’ın tüm döngüleri lowering mekanizması ile while döngüsüne dönüştürdüğünü biliyor muydunuz? Ben de bilmiyordum.

Lowering mekanizması C# compiler’ın sıklıkla kullandığı bir yapı. Bunu özellikle “Compiler magic” veya Syntactic sugar dediğimiz durumlarda kullanılıyor. Ek olarak daha kompleks mekanizmaları, MSIL ve makine koduna daha rahat çevrilebilmesi için basit mekanizmalara çeviriyor.

Eric Lippert şöyle diyor;

“Bununla birlikte, derleyicinin aynı dilde yüksek seviyeli dil özelliklerinden düşük seviyeli dil özelliklerine “düşürülmesini” sağlamaktır.”

C# compiler’ı aşağıdaki tüm döngüleri arka tarafta while döngüsünün muadili olacak şekilde çeviriyor;

✅ for döngüsü

✅ foreach döngüsü

✅ while döngüsü

✅ do while döngüsü

Belki de bu döngüler sadece biz developer’lar için vardır.

Örneğin;

for(var i = 0; i less 5; i++)
{
}

şeklinde bir kod yazdığımızda C# compiler bu kodu;

int num = 0;
while (num less 5)
{
    num++;
}

olarak lower ediyor.

Ek olarak lowering mekanizması sadece çevrimlerden ziyade, normalde developer’ın yazması gereken kodları kendisi yazarak developer’ın çok daha az kod yazarak işini halletmesine yardımcı oluyor.

Lowering çok önemli bir konu bu arada. Derinlemesine bilmeseniz ile sharplab.io gibi mekanizmaları düzenli olarak kullanmanız size çok faydalı olacaktır.

Kaynaklar:

1️⃣ https://mattwarren.org/2017/05/25/Lowering-in-the-C-Compiler/

2️⃣https://github.com/dotnet/roslyn/tree/main/src/Compilers/CSharp/Portable/Lowering

C# 10 Yenilikleri – Tek overload’u olan delegate türü

C# 10 ile birlikte, derleyici, yalnızca tek bir overload’a sahip olan delegate’lerin türünü çıkarabiliyor.

Örneğin;

// int
var i = Console.Read();

// Func int
Func int f = Console.Read;

Bu birden fazla overload’u olan Console.WriteLine’da çalışmaz mesela;

var str = Console.WriteLine; // Hata

Yani artık C# 10 ile şu şekilde delegate’ler yazabiliyoruz;

var str = (int i) => i.ToString();
str(42);

C# 10 Yenilikleri – CallerArgumentExpression attribute kullanımı

C# 10 ile gelen yeniliklerden biri de CallerArgumentExpression attribute kullanımı.

CallerArgumentExpression attribute kullanımı size parametre olarak verdiğiniz expression’ın string halini size geri döndürüyor. Diğer compiler servis attribute’ler gibi bu da optional.

using System.Runtime.CompilerServices;

void Write(object obj, [CallerArgumentExpression("obj")] string? msg = null)
{
    Console.WriteLine($"Expression şu: {msg}");
}

Write(new object());
Write("Merhaba");
Write(42 + 42 + 42);
Write(() lambda { });

int i = 42;
Write(i);

// Expression şu: new object()
// Expression şu: "Merhaba"
// Expression şu: 42 + 42 + 42
// Expression şu: () lambda  { }
// Expression şu: i

Kaynak: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/caller-argument-expression

C# 10 Yenilikleri – Struct ve Anonymous type’larda with kullanımı

C# 10 ile gelen yeniliklerden biri de struct ve anonymous type’larda gelen with kullanımı.

C# 9 ile hayatımıza with expression’ı girmişti. With ile solundaki operand’ın property ve field’ları ile, sağında verilen özellikler göz önüne alınarak yeni bir kopyasını oluşturur. C# 9 ile gelen with kullanımı sadece record tipi için geçerliydi.

Kısaca bir kullanım olarak;

var p1 = new Person("Soner", "İstanbul" );
// p1.Name: Soner
// p1.City: İstanbul

var p2 = p1 with { City = "Kırklareli" };
// p2.Name: Soner
// p2.City: Kırklareli

public record Person(string Name, string City);

C# 10 ile artık struct ve anonim tiplerde de with kullanımına destek gelmiş oldu. Struct için aynı kodu şu şekilde değiştirebiliriz;

var p1 = new Person();
p1.Name = "Soner";
p1.City = "İstanbul";
// p1.Name: Soner
// p1.City: İstanbul

var p2 = p1 with { City = "Kırklareli" };
// p2.Name: Soner
// p2.City: Kırklareli

public struct Person
{
    public string Name;
    public string City;
}

Anonim tiplerde de aynı şekilde kullanabiliriz;

var p1 = new { Name = "Soner", City = "İstanbul" };
// p1.Name: Soner
// p1.City: İstanbul
var p2 = p1 with { Name = "Ahmet" };
// p2.Name: Ahmet
// p2.City: İstanbul

Kaynak: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/with-expression

C# 10 Yenilikleri – Mix declarations and variables in deconstruction

C# 10 versiyonu ile gelen özelliklerden biri de “mix declarations and variables in deconstruction” kavramı.

C# 9 ve öncesi versiyonlarda deconstruction‘lar içerisinde ya hepsinin daha önceden tanımlanmış olması, ya da hepsinin yeni tanımlanıyor olması gerekiyordu.

(int, int, int) rgb = (255, 255, 0);
(int first, int second, int third) myRgb = rgb;

ya da;

int r1, g1, b1;
(r1, g1, b1) = rgb;

şeklinde tanımlanabiliyordu. C# 10 ile birlikte, bu tanımlamaların bir kısmını orada, bir kısmını ise daha öncesinde tanımlayabiliyorsunuz.

Yani;

int r2, g2;
(r2, g2, int b2) = rgb;

gibi. Gördüğünüz gibi r2 ve g2 değişkenlerimi kodumun farklı bir yerinde tanımlayıp, b2 değişkenimi deconstruction içinde tanımlayabiliyorum.

Kaynak: https://devblogs.microsoft.com/dotnet/welcome-to-csharp-10/#mix-declarations-and-variables-in-deconstruction