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.
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
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.
C# 9 ve önceki versiyonlarda Visaul Studio içerisinde yeni bir sınıf oluşturduğunuzda namespace’ler “Blocked scoped” olarak ekleniyordu. Yani namespsace’lerde süslü parantezler içerisinde ve tüm class’lar da soldan bir tab (veya 4 boşluk) olacak şekilde hizalanıyordu.
namespace NET6
{
internal class Class1
{
}
}
C# 10 versiyonu ile hayatımıza File scoped namespaces kavramı girdi. Bu sayede namespace tanımlamalarındaki gereksiz süslü parantezlerden ve bu namespace’in içerisindeki her bir satır kodun başındaki gereksiz boşluklardan kurtulma imkanı sunuldu geliştiricilere. Bunun için .editorconfig dosyası içerisinde “Code Style” sekmesindeki “Namespace declarations” özelliğini “File scoped” olarak değiştirmeniz yeterli. Tabi bu değişiklik var olan sınıflarınızı etkilemiyor, yeni eklediğiniz sınıflarda geçerli.
C# 8 ile hayatımıza property pattern özelliği girmişti hatırlarsanız, iç içe olan property’lere süslü parantezler kullanarak erişebiliyorduk.
if (p is Person { Name: "Soner", Address: { City: "İstanbul" } })
Ama bu kullanım, bir çok iç içe property içeren yapılarda çok fazla süslü parantez olması nedeniyle okunurluğu azaltıyordu.
C# 10’da gelen extended property patterns ile, artık süslü parantez kullanmadan direkt olarak nokta notasyonu ile bu property’lere erişim sağlayabiliyoruz pattern’lar içerisinde.
if (p is Person { Name: "Soner", Address.City : "İstanbul" })
Bu yapıda da iç içe property’lerde null kontrolü var. Yani burada Address property’si null olsa bile herhangi bir nullreferenceexception fırlatmıyor.
Bunun nedeni de string interpolation arka tarafta string.Format çağırıyordu ve bir method çağırımı compile time’da constant olamayacağı için hata veriyordu doğal olarak. Yani string interpolation içerisinde kullandığımız değer constant olsa bile, string interpolation yapısının “kendisi” constant değildi.
Bunun legal olabilmesi için C# community’den çok fazla talep gelince C# 10 versiyonunda bu özelliği de eklemişler. Bunu artık hem yukarıdaki gibi const değerleri içerisinde, hem de attribute’ler içerisinde kullandığımız mesajlarda kullanabiliyoruz.
[Obsolete($"{Name} bunu kullanamaz!")]
void MyMethod()
{
}
.NET 6 ile hayatımıza implicit namespace kavramı girdi. Bu sayede projelerde gelen “boilerplate” içerisindeki using karmaşasından kurtulmak hedeflendi. Namespace’leri artık “global using” şeklinde projenizde tek bir .cs dosyası içerisinde veya “\..\obj\debug\net6.0” klasöründeki GlobalsUsing.cs dosyası içerisinde saklayabiliyorsunuz.
Dikkat edilmeli ki bu GlobalsUsing.cs dosyası otomatik oluşturulan bir dosya.
Örneğin projenizdeki her bir cs dosyası içerisinde System.Data namespace’ini kullanmak zorunda olduğunuzu düşünün. Her bir .cs dosyasının tepesinde bunu yazmak yerine, .csproj içerisinde ImplicitUsings tag’ini enable ederek, tek bir cs dosyası içerisinde “global using” tanımlayarak ya da bahsettiğim GlobalsUsing.cs dosyası içerisinde tanımlayarak tüm projenizde o namespace’in aktif olmasını sağlayabiliyorsunuz.
Ayrıca .csproj dosyası içerisinde ItemGroup olarak ta bu namespace’leri Using Include veya Using Remove kullanarak ekleme veya çıkartabiliyorsunuz.
RC1 ile birlikte, .NET 6 projesi oluşturduğunuzda .csproj dosyası içerisinde default olarak ImplicitUsings enable olarak geliyor. Dilerseniz bunu .csproj dosyanız içerisinde DisableImplicitNamespaceImports tag’ini true olarak disable edebiliyorsunuz.
Scott Hanselman‘ın söylediğine göre C# 10 projeleri bu sayede daha hızlı bir şekilde ayağa kalkıyorlarmış.
Her bir proje SDK tipinin default namespace değerleri şu şekilde;