Bildiğiniz üzere C# 10, Visual Studio 2022 ve .NET 6 ile kullanıma sunuldu. Bu makalemde, Microsoft’un kodlarımızı daha güzel, daha anlamlı ve daha hızlı hale getirecek özellikler olarak tanımladığı bu yenilikleri ele alacağız. 😊
- Record enhancements
C# 9 ile hayatımıza giren record’lar için C# 10’da bazı yenilikler getirildi. class anahtar sözcüğü ile bir record’un referans tipte olup olmadığını ayırt edebiliriz. Temel olarak Resim – 1’deki ve Resim – 2’deki record tanımlamaları aynıdır.
Resim – 1
Resim – 2
Bu özelliğin yanı sıra artık bir record’u struct olarak tanımlayabileceğiz. Bildiğiniz üzere class bir referans tiptir ve heap bellekte tutulur. Struct bir değer tiptir ve stack dediğimiz bellekte tutulur. Performans açısından daha verimli bir record tanımlaması yapmak istediğimizde record struct’ları kullanabiliriz.
Resim – 3
Record struct özelliklerine değinmek gerekirse;
Bir record struct tanımlarken init – only properties kullanabiliriz. C# 9’dan hatırladığınız üzere init ile tanımlanan bir property’nin değerini ilk atamadan sonra değiştirmemiz mümkün değildi. Resim – 4’teki örnekte gördüğünüz gibi init keyword’üne sahip bir property’e tekrar atama yaptığımızda derleme hatası almaktayız.
Resim – 4
Yine C# 9’dan aşina olduğumuz with ifadesini record struct’lar ile kullanabiliriz.
Resim – 5
İki record struct için eşitlik karşılaştırması yapabiliriz. Eğer bu nesneler aynı değerleri tutuyorsa eşitlik karşılaştırmasının değeri true olacaktır.
Resim – 6
Resim – 7
Record struct’larda ToString method’u override edilerek oluşturduğumuz record struct nesnesinin string halini elde edebilmemiz sağlanmıştır.
Resim – 8
Resim – 9
Ancak, aşağıdaki örnekte gördüğünüz gibi bu işlemi struct nesnesi için yapabilmemiz mümkün değildir.
Resim – 10
Resim – 11
- Global and implicit usings
Global Using Directives
Bu özellikle beraber bir using directive’i global anahtar kelimesiyle birlikte tanımladığımızda bu directive, proje içerisindeki her C# dosyası için otomatik olarak kullanılabilir hale gelmektedir.
Resim – 12
global anahtar kelimesini bir using static directive ile ya da bir alias tanımlarken kullanabiliriz.
Resim – 13
Implicit usings
.NET 6 ile yeni bir console uygulaması oluşturduğumuzda Program.cs içinde sadece Console.WriteLine(“Hello, World!”); satırı ile karşılaşıyoruz. Peki burada using System; olmadan Console.WriteLine methodunu nasıl kullanabiliyoruz?
Resim – 14
CSharp10Features.csproj dosyasına gittiğimizde ImplicitUsings isimli yeni bir özellikle karşılaşmaktayız. Yukarıdaki sorumuzun cevabı tam olarak burada bulunuyor.
Resim – 15
ImplicitUsing özelliği enable iken CSharp10Features > obj > Debug > net6.0 altındaki CSharp10Features.GlobalUsings.g.cs içerisinde bulunan namespace’ler o proje içindeki tüm .cs dosyaları tarafından kullanılabilir.
Resim – 16
CSharp10Features.GlobalUsings.g.cs dosyasını incelediğimizde, uygulamamızın her C# dosyasında otomatik olarak kullanılabileceğimiz tüm namespace’leri görebiliriz.
Resim – 17
- Improved Definite Assignment Analysis
Definite Assignment Analysis değişkenlerin kullanılmadan önce atanmasını sağlamak için compiler tarafından yapılan statik analizdir. C# 10’dan önce, definite assignment (kesin atama) ve null-state analizinin false positive hatalar ürettiği durumlar mevcuttu. Bu hatalar genellikle bool sabitleriyle karşılaştırmalar, conditional access ile sabit değer arasındaki karşılaştırma ve null coalescing ifadeleriyle ilgiliydi. Bu case’leri şu link üzerinden inceleyebilirsiniz: Improved Definite Assignment Analysis
- Extended property patterns
Pattern’lardaki nested (iç içe) property değerlerine erişimi kolaylaştırmak adına Extended Property Patterns özelliği getirildi. Örneğin, elimizde Employee ve Department isimli classlarımız olsun.
Resim – 18
Bir employee nesnesi oluşturalım ve bu nesnenin DepartmentName özelliğine erişmek istediğimizi varsayalım. Bu işlemi C# 10 öncesi süslü parantezler ile sağlıyorduk ancak birden fazla nested property içeren yapılarda bu kullanım okunabilirliği oldukça azaltmaktaydı.
Resim – 19
C# 10’da bu işlemi nokta gösterimi ile daha basit ve okunabilir hale getirebiliriz.
Resim – 20
- Record types can seal ToString()
Record’lar için C# 10’da yeni bir özellik daha getirildi. C# 9’da ToString methodunun override edilmesini önlemek için sealed olarak tanımlamayı denediğimizde aşağıdaki hatayı almaktaydık.
Resim – 21
C# 10’da ise ToString methodunu sealed olarak tanımladığımızda herhangi bir hata almıyoruz:
Resim – 22
- Mix declarations and variables in deconstruction
Bir nesneyi parçalara ayırma ve bunları yeni değişkenler olarak atayabildiğimiz Deconstruction mantığı için yeni bir özellik getirildi. Önceki sürümlerde deconstruction işlemi sırasında tüm değişkenlerin ya işlem esnasında initialize edilmesi ya da hepsinin önceden initialize edilmiş olması gerekiyordu. C# 10 ile bu kısıtlama kaldırılmış oldu.
C# 9 ve öncesi:
Resim – 23
Resim – 24
C# 10:
Resim – 25
- Async method builder override
C# 7 ve sonrasında AsyncMethodBuilderAttribute yalnızca class, struct, enum, interface ve delegate tanımlamaları için kullanılabilirken bu attribute, C# 10’da bir method için de uygulanabilir hale gelmiştir.
C# 9 ve öncesi:
Resim – 26
C# 10:
Resim – 27
- Enhanced #line directives
#line directive, derleyici tanılama ve hata ayıklama bilgilerinde raporlanacak orijinal dosya adını ve satır numarasını belirtmek için genellikle oluşturulan kodda (örneğin, Razor dosyalarından oluşturulan kod) kullanılır. C# 10’da bu directive, satırdaki konumu da belirtecek şekilde genişletilmiştir. Daha detaylı bilgi için şu linki inceleyebilirsiniz: Enhanced #line directives
- Lambda improvements
C# 10, üç yeni Lambda syntax iyileştirmesini bizlere sunuyor:
Infer a natural delegate type for lambdas and method groups
Tıpkı bir method veya local function’da yaptığımız gibi bir lambda ifadesinin dönüş tipini var keyword’ü ile belirtebiliriz.
C# 9:
Resim – 28
C# 10:
Resim – 29
C# 10 ile bazı method grupları da var keyword’ü ile tanımlayabiliriz. Örneğin, Console.Read’i tek bir overload’a sahip olduğu için var ile tanımlayabiliriz ancak Console.Write birden fazla overload’a sahip olduğundan dolayı, derleyici değişkenin türünü belirleyemez ve aşağıdaki hatayı alırız.
Resim – 30
Resim – 31
Allow lambdas with explicit return type
Bir lambda’nın dönüş tipini explicit olarak belirtebiliriz. Resim – 32’deki örnekte a değişkeninin tipi Func<int> iken b değişkeninin tipi Func<long> olacaktır.
Resim – 32
Allow lambdas with attributes
Lambda ifadelerine ve lambda parametrelerine attribute’lar ekleyebiliriz.
Resim – 33
- Interpolated string improvements
Interpolated string handlers
Interpolated string handlers özelliği sayesinde varsayılan string interpolation işlemine bağlı kalmak yerine, kendimiz custom string interpolation oluşturabileceğiz.
Custom string interpolation oluşturabilmek için;
- InterpolatedStringHandlerAttribute ile bir struct ya da class bildirmemiz,
- İlgili string’in sabit karakterlerinin uzunluğunu (literalLength) ve biçimlendirilecek parametre sayısını (formattedCount) alan bir constructor (burada parametre sayısını arttırarak method’umuzu daha da özelleştirebiliriz) oluşturmamız,
- Sabit karakterleri elde etmek için bir AppendLiteral(string s) methodu oluşturmamız
- Parametreleri işlemek için ise bir AppendFormatted<T>(T t) methodu oluşturmamız gerekir.
Resim – 34’te bu kuralların uygulandığı basit bir örneği görüyorsunuz.
Resim – 34
Örnek method’umuzu console uygulamasında çalıştırdığımızda Resim – 36’daki çıktıyı elde ediyoruz.
Resim – 35
Resim – 36
Constant interpolated strings
C# 9 ve önceki versiyonlarda const olarak tanımlanan değişkenler içerisinde aşağıdaki gibi bir birleştirme işlemi yapmak istediğimizde Concatenation kullanmak zorundaydık.
Resim – 37
C# 10 ile artık const bir değişkeni String Interpolation kullanarak tanımlayabilmekteyiz.
Resim – 38
Bu özellik değişken tanımlamanın yanı sıra daha fazla yerde String Interpolation kullanımına olanak sağlıyor. Örnek olarak attribute’lar ile çalışırken Constant Interpolated String’i rahatlıkla kullanabiliriz.
Resim – 39
- File – scoped namespace
Oldukça basit gözüken ama kod okunaklığını ve yazım kolaylığını attıracak özelliklerden biri olduğunu söyleyebiliriz. Bu özellikle namespace tanımlamalarını tek satır halinde yazabilmekteyiz.
C# 9 ve öncesi:
Resim – 40
C# 10:
Resim – 41
- Parameterless struct constructors
C# 10 öncesinde, bir struct içerisinde default yani parametre almayan bir constructor tanımladığımızda “Structs cannot contain explicit parameterless constructors” hatasını almaktaydık. C# 10’da ise propertylerimizi initialize etmek şartıyla struct içinde default bir constructor oluşturabiliriz:
Resim – 42
- Caller expression attribute
CallerArgumentExpressionAttribute, bir method’un bağlamı hakkında bize bilgi sunar. Daha basit bir ifadeyle bir method’a ait bir parametrenin değerini string olarak yakalayabiliriz. Diğer CompilerServices attribute’ları gibi, CallerArgumentExpression da isteğe bağlı bir parametreye uygulanır. Bu özellik bir hatanın kaynağı hakkında daha iyi bilgi sağlamak ve ilgili hatayı loglamak için kullanılabilir. Örneğin Resim – 43’teki CheckCondition methodunu inceleyelim.
Resim – 43
Bu method’a bir aşağıdaki gibi sonucu false dönecek bir değer ilettiğimizde, CallerArgumentExpression içerisinde belirttiğimiz parametrenin yani condition’ın string ifadesi conditionExpression parametresine set edilir.
Resim – 44
Bir console uygulamasında bu methodu çağırdığımızda aşağıdaki çıktıyı elde etmekteyiz.
Resim – 45
Bu makalemde C# 10 yeniliklerinden her birine kısaca değinmeye çalıştım. Eğer konu hakkında daha fazla bilgiye ulaşmak isterseniz Microsoft’un konu hakkındaki dokümanını inceleyebilirsiniz: What’s new in C# 10
Faydalı olması dileğiyle. 😊
Bu konuyla ilgili sorularınızı alt kısımda bulunan yorumlar alanını kullanarak sorabilirsiniz.
Referanslar
https://devblogs.microsoft.com/dotnet/welcome-to-csharp-10/
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10
TAGs:.NET 6, C# 10 new features, C# 10