loading...
Cover image for Csharp 9 ve sonrası

Csharp 9 ve sonrası

canalpay profile image Can Alpay Çiftçi ・7 min read

Bu yazım daha önce hazırlanmış bu blogda da yayınlanmıştır.

Merhaba, bu blog yazısında C# 9 ile gelmesi beklenen yeni özelliklere bir bakış atacağız ve bazen de bu özellikler ile ilgili kişisel düşüncelerimi paylaşacağım. Lütfen eksik\hatalı bulduğunuz kısımları yorum\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 ziyaret edebilirsiniz.

Hedef Türünde new İfadesi

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 olarak

    public class C 
    { 

        private readonly static Point point = new(); 
        public Point Target => new (4,5); 
        //    var newList = new List<int>(); Hala sözdizimi 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ş.). 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 dizimin kullanılayamacağı başka alanlarda kullanılmaacak 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

Bazı şeyler

*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

Fonksiyonsa basit bir belirtme ile artık parametre de geçilen değerin null olup olmadığı doğrulaması yapılacabilecek.
Örnek olarak

// Önce 
void Insert(string s) 
{ 
  if (s is null) 
    throw new ArgumentNullException(nameof(s));
  ... 
} 

// Sonra 
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şleni 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 lambdalarda da kullanılabilecek bir biçimde genişletiliyor.
Örnek olarak

(_, _) => 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 olarak

 public class Startup
 {
        ...

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting(routes =>
            {
                Task SayHello(HttpContext context)
                {
                    return context.Response.WriteAsync("Hi there!");
                }
                routes.Map("/", SayHello);

                [Authorize(Roles = "admin")]
                Task AdminsOnly(HttpContext context, [FromServices] ApplicationContext db)
                {
                    // get message from db and write it out
                }
                routes.Map("/secret", AdminsOnly);
            });

            app.UseAuthentication();
            app.UseAuthorization();
        }
    }

Örnek buradan alınmıştır.

Fonksiyon pointerlarının eklenmesi

Artık Csharpta da unsafe kod olarak fonksiyon pointerları kullanılabilecek.

Örnek olarak

unsafe class Util 
{ 
    public static void Log() { } 
    void Use() 
{ 
        delegate*<void> ptr1 = &Util.Log; 

        // hata: "delegate*<void>"  türü "delegate*<int>" türü ile uyumlu değil; 
        //delegate*<int> ptr2 = &Util.Log; 
        // Sorunsuz. void*'e dönüşüm her zaman izin verilmiştir. 

        void* v = &Util.Log; 
   } 
} 

Pattern Matching Geliştirmeleri

Tür Deseni

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

void M(object o1, object o2) 
{ 
    var t = (o1, o2); 
    if (t is (int, string)) {} //  o1 int ve o2 string ise

    switch (o1) 
    { 
        case int: break; // o1 int ise
        case System.String: break; // o1 string ise
    } 
} 

İlişkisel eşleme

Artık <, >, >=, <= operatörler aşağıdaki örnekteki gibi kullanılabilecek. Ancak yalnızca const değeler ile kullanılabilecek.

 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 ve 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.

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.

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(değeri değişebilir) olmak zorundadır. Immutable(sabit) 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.

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

Records

Record tipleri referans tipindedir. 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 propertysinin değeri 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.

1.Veri üyeleri olarak belirtme:
Burada sıralama önemli değildir. Sadece üyeler vardır.

public data class Person { string FirstName; string LastName; } 

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

public data class Person(string FirstName, string LastName);
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)... 
} 

Extension GetEnumerator

Foreach döngülerinin extension GetEnumerator methodlarını tanımasını sağlar.

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.

using System.Runtime.CompilerServices; 

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

Attribute kullanılabilmesi için bazı gereklilikler vardır:
1.Kullanılacak method static olmalı.
2.Parametresiz olmalı.
3.Void dönüş değerine sahip olmalı
4.Generic olmamalı ya da generic type içermemeli.
5.İçerdiği modülden method erişilebilir olmalı.(local fonksyion olmamalı ve internal veya public erişicileri ile işaretlenmiş olmalı)

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...)

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ılabilecektir.

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; 

Discussion

pic
Editor guide
 

Ellerine sağlık hocam. Bir Türk içeriği görünce burada hemen girdim. Tekrardan ellerine sağlık.