C# 9.0

Merhaba, bu blog yazısında C# 9.0 ile gelmesi beklenen yeni özelliklere bir bakış atacağız, bazen de bu özellikler ile ilgili kişisel düşüncelerimi paylaşacağım. Lütfen eksik ya da hatalı bulduğunuz kısımları yorum veya e-posta yolu ile belirtmekten çekinmeyin.

C# 9 ile gelmesi beklenen özellikleri yazarken, https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md adresindeki dil özelliklerinin 'durumunun' paylaşıldığı sayfadan yararlanacağım.


Özellikler için son durum, güncelemeler ve daha fazla bilgi için yukarıdaki linkten ziyaret edebilirsiniz.


Hedef türünde new ifadesi


Var anahtar sözcüğü(keyword) ile kullanılan tür çıkarsaması(type inference) sadece yerel(local) değişkenler ile kullanılabiliyordu. Artık sınıfa ait alanlar için de tür çıkarsaması kullanılabilecek. Ancak farklı söz dizimi ile.


Örnek C#


public class C
{
   private readonly static Point point = new();
   public Point Target => new (4,5);
   //var newList = new List<int>(); Hala söz dizimi hatası
 
   public static void Main(string[] a)
   {
     List<int> list = new();
     var newList = new List<int>();
   }
}

public class Point
{
   public double X {get;set;}
   public double Y {get;set;}
   public Point(){}
   public Point(double x, double y)
   {
      (X,Y) = (x,y);
   }
}

Bu yeni tür çıkarsama sözdizimi hem yerel, hem de sınıfa ait üyelerde kullanılabilecek.

Var tür çıkarsamasının kullanılmamasının en büyük nedenlerinden biri derleyicide gerçekleştirimin zor olması kaynaklı olarak gözüküyor(Derleyicinin bazı kısımları buna uygun olarak yeniden yazılması gerekecekmiş deniyor). Ayrıca apideki tür değişimleri daha belirgin olarak görüleceğini de eklemişler.


Kişisel görüşüm, yerel değişkenlerde de fonksiyon değerinin sonucu için kullanılmayacaksa, ve bu yeni söz diziminin kullanılayamacağı başka alanlarda da kullanılmayacak ise bu kullanımın daha okunabilir olduğunu ve IDE kullananlar için daha doğru değişken adı önerilerini verebileceğini düşünüyorum.


Örneğin, throw new şeklinde kullanılabilecek(System.Exception) ref olarak belirtilemeyecek. Binary operatörler ile kullanılamayacak.


Tür bildiriminde ‘ref’ ve ’partial’ olarak nitelendirmekte sıralama rahatlığı


Önceden örneğin struct bildirimi yaparken ref partial struct olarak belirtmek gerekiyor idi.(ref hemen partialdan önce olacak şekilde) Ancak artık ref partial sıralamasını serbest bir biçimde bildirebileceğiz.


Parametre null mı? doğrulamasını kolaylaştırma


Fonksiyon ise, basit bir belirtme ile artık parametre de geçilen değerin null olup olmadığı doğrulaması yapılacabilecek.

Örnek C# kodu


// Önce
void Insert(string s) {
  if (s is null)
   throw new ArgumentNullException(nameof(s));
 
  //...
}
 
// Sonra (s'nin kullanımına dikkat!)
void Insert(string s!) {
  //...
}

Yerel ilklemeleri geç


Bazı durumlarda değişken kullanılmayacak ve/veya kullanılacağı zaman ise zaten başka bir değer ile setlenecektir. Bu gibi zamanlarda bazen derleyici bu durumu yakalayamaz ve yine de değişkeni ilklendirir. Bu da anlamsal olarak bir zararı olmasa bile performans kaybına neden olur. Böyle durumlar için SkipLocalsInit Attribute’ü ekleniyor. Bu sayede attribute ile belirtimiş üyeler ilklendirilmeyecek.


Artık lambda parametreleri


Eklenen discard özelliği 'lambda'larda da kullanılabilecek bir biçimde genişletiliyor.


Örnek C# kodu


(_, _) => 1, (int _, string _) => 1, void local(int _, int _) ..

Native Int’ler


Int karşılığında => nint

uint karşılığında ise => nuint türleri ekleniyor. Bu türler int gibi genel tür olmadığı gibi işlemci üzerinde büyüklükleri değişebilecek değerler. Bu taşınabilirliğe sıkıntı getirdiği gibi performans da getirmekte.


Yerel Fonksiyonlarda Attribute kullanabilme


Yerel fonksiyonlarda da artık Methodlardaki gibi attribute kullanılabilecek.

Örnek kod parçası olarak aşağıdaki linke göz atmanızı öneririm:

https://gist.github.com/rynowak/4d4738a57fb482952056ca67573f1d50


Fonksyion pointerlarının eklenmesi


Artık Csharp'ta da unsafe kod olarak fonksiyon 'pointer'ları kullanılabilecek.


Örnek C# kodu

unsafe class Util {
  public static void Log() { }
 
  void Use() {
  delegate*<void> ptr1 = &Util.Log;
 
   //hata: "delegate*<void>" türü "delegate*<int>"türü ile uyumsuz
   //delegate*<int> ptr2 = &Util.Log;
 
   //Okay. Conversion to void* is always allowed.
   void* v = &Util.Log;
  }
}

Birazda Pattern Matching geliştirmelerine göz atalım,


Tür Deseni


Artık direkt olarak basitçe türün kendisini belirterek yanınca discard(_) belirteci olmadan kullanılabiliyor.


Örnek C#

void M(object o1, object o2)
{
   var t = (o1, o2);
   if (t is (int, string)) {} 
   // test if o1 is an int and o2 is a string
   switch (o1) {
   case int: break; // test if o1 is an int
   case System.String: break; 
   // test if o1 is a string
 }
}

İlişkisel eşleme


C#'ın bu versiyonu ile <, >, >=, <= operatörler aşağıdaki örnekteki gibi kullanılabilecek. Ancak yalnızca const değeler ile kullanılabilecek.


Örnek C#

public static LifeStage LifeStageAtAge(int age) => age switch
{
   < 0 => LiftStage.Prenatal,
   < 2 => LifeStage.Infant,
   < 4 => LifeStage.Toddler,
   < 6 => LifeStage.EarlyChild,
   < 12 => LifeStage.MiddleChild,
   < 20 => LifeStage.Adolescent,
   < 40 => LifeStage.EarlyAdult,
   < 65 => LifeStage.MiddleAdult,
   _ => LifeStage.LateAdult,
};

Desen Birleştirici


2 farklı desen eşleşmesi artık and, or, not ifadeleri ile birleştirilebilecek. and ifadesi hesaplanırken or işlemine göre öncelikli olarak hesaplanıyor. Ancak parantezler yardımıyla öncelikler değiştirilebilir.


Örnek C#


bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');

Static lambda


Lambda kullanılırken yanlışlıkla yerel değerlere(this ve base 'e) erişiminin engellenmesi amaçlı bir özellik.


Örnek C#

int y = 10; someMethod(static x => x + y); // hata!
const int y = 10; someMethod(static x => x + y); // sorunsuz :-)

Init Only Setters


Property tanımlarının nesne ilklendirmeler ile (new A{Property = “A”}) çalışabilmesi için mutable olmak zorundadır. Immutable tanım ile nesne ilklendirme kullanılabilmesi için set ve get harici üçüncü bir erişici tanımlanıyor. Bu tanım sayesinde property bir kez setlendikten sonra tekrar setlenemeyecek ve nesnenin immutable olarak kullanılabilmesine olanak sağlanacaktır.


Örnek C#

public class Person
{
   public string FirstName { get; init; }
   public string LastName { get; init; }
}

Records


Record tipleri referans tipindedir. (https://github.com/dotnet/csharplang/issues/3166#issuecomment-648831123). Varsayılan olarak immutable veri tanımlamak için kullanılır.

Record tanımlanırken varsayılan olarak değer üzerinden karşılaştırma yapan Equals ve GetHashCode() otomatik olarak tanımlanacaktır. Sealed olarak işaretlenmediyse bu methodlar ezilebilir.

Kendi türünü alan copy constructor’a sahiptir. Ayrıca parametresiz virtual clone methoduna sahiptir.

Miras alma vardır. With ifadesi ile var olan nesnenin istenilen propertysi değişmiş yeni bir nesne elde edilebilir.



public data class Person { string FirstName; string LastName; }
public data class Student : Person { int ID; }
Person person = new Student { FirstName = "Scott", 
                              LastName = "Hunter", 
                              ID = GetNewId() 
                             };
otherPerson = person with { LastName = "Hanselman" };

Records tanımlamanın iki yöntemi vardır.


Veri üyeleri olarak belirtme:


public data class Person { string FirstName; string LastName; }
  
//Burada sıralama önemli değildir. Sadece üyeler vardır.

Pozisyonel tanımlama:


Burada sıralama önemli olduğu ve belirtildiği için Constructor’ı veDeconstruct methodu da otomatik olarak tanımlanmıştır.


var person = new Person("Scott", "Hunter"); // pozisyonel construction
var (f, l) = person; // pozisyonel deconstruction

Hedef Türünde Koşulsal İfade


c ? e1 : e2 gibi bir koşulsal ifade de e1 ve e2 için ortak bir tür yok ise, veya varsa da ortak türe implicit bir dönüşüm yoksa e1’den T’ye ve ayrıca e2’den T’ye implicit dönüşüm sağlayan implicit ifade tanımlanır.


Covariant Return Types


Türetilmiş bir sınıfın bir fonksiyonunu ezerken dönüş değerini daha kısıtlayıcı olabilecek şekilde ezebilmeyi amaçlar.


class Compilation
{
  virtual Compilation WithOptions(Options options)...
}
class CSharpCompilation : Compilation
{
  override CSharpCompilation WithOptions(Options options)...
}

Module İlklendiriciler


Dotnet platformunda olsa da C# da olmayan bir özellik. Bir modül ilklendirici assembly ilk yüklendikten sonra ilk çalışan fonksiyondur. Static Constructor’ın class için değil modül (assembly)için uygulanmış hali denebilir.


Örnek kod:


class C
{
  [ModuleInitializer]
  internal static void M1()
  {
     //...
  }
}

Attribute kullanılabilmesi için bazı gereklilikler vardır. Kullanılacak method static olmalı.

Parametresiz olmalı. Void dönüş değerine sahip olmalı, generic olmamalı ya da generic type içermemeli. İçerdiği modülden method erişilebilir olmalı(local fonksyion olmamalı ve internal ya da public erişicileri ile işaretlenmiş olmalı gibi).


Partial Method Geliştirmeleri


Partial methodlar için artık açıkça private, public gibi erişici etiketleri kullanılabiliecek. Eğer erişiciler kullanıldı ise tanım ve belirtim tarafında da aynı şekilde belirtmek gerekecek.

Partial method üzerindeki diğer kısıtlamalar da ayrıca kaldırılmaktadır(void olmayan türde dönüş, ref, out parametreleri, extern belirteci...).


Örnek C#

partial class D
{
   internal partial bool TryParse(string s, out int i); 
}
 
partial class D
{
   internal partial bool TryParse(string s, out int i) { }
}

Ref, out gibi keywordler kullanılırken erişiciler açıkça belirtimelidir. Belirtilmemesi hata nedeni olacaktır. Partial methodlar override ve interface ile de kullanılabilecekler.


Örnek C#

interface IStudent
{
   string GetName();
}
 
partial class C : IStudent
{
   public virtual partial string GetName(); 
}
 
partial class C
{
   public virtual partial string GetName() => "Jarde";
}

Üst Seviye İfadeler


Bazı zamanlar sadece yeni bir şey öğrenmeyi denerken ya da var olan bir şeyin nasıl çalışıtığını incelerken çok basit programlar yazarız. Bunları yazarken standart olarak;


static class Program
{
   static async Task Main(string[] args)
   {
     //ifadeler
   }
}

Şeklinde Main’in içinde yazmamız gerekmekte. Artık basit denemeler için direkt olarak ifade yazılarak program derlenip çalıştırılabilecek.


Örnek olarak:


using System;
Console.WriteLine("Test");
return 1;


Can Alpay Çiftçi

Technology/Code Geek

canalpayciftci@gmail.com