.Net Core’da C# 7.0 Pattern Matching
Selamlar Arkadaşlar,
Bugün .Net Core Console bir uygulamada, C# 7.0 ile gelen benim de iş hayatında bolca kullandığım Pattern Matching üzerine farklı örnekeler ile konuşacağız. Öncelikle alttaki komut ile ilgili uygulama oluşturulur.
1 |
dotnet new console -o patternmatching |
İlk başta gelin alttaki gibi bir Person sınıfı oluşturalım.
1 2 3 4 5 6 7 8 9 10 11 12 |
public class Person { public Person(string name, string surname, DateTime birthDate) { this.Name = name; this.Surname = surname; this.Birthdate = birthDate; } public string Name { get; set; } public string Surname { get; set; } public DateTime Birthdate { get; set; } } |
“is” Operator’ünü Pattern Matching ile kullanma:
Bu operatör C#’ın ilk verisyonundan beri, alınan objenin belirlenen bir türle uyumlu olup olmadığını konturol etmek için kullanılır. Yani dönüştürülecek nesnenin, istenen türe ait interfaceden ya da base class’dan türeyip türemediğine bakar. Dönen değer “true” veya “false” tur. C# 7 ile artık “is” operatörü, desenleri kontrol etmek için de kullanılabilir. Aşağıda farklı tiplerde eleman barındıran bir object[ ] array data dizisi bulunmaktadır.
1 |
object[] data = { null, 39, DateTime.Now, null, new Person("Bora", "Kasmer", new DateTime(1978, 6, 3)), 78, new Person("Duru", "Kasmer", new DateTime(2012, 2, 27)) }; |
- Const Pattern : İlgili pattern’de, direk bir sayı veya null değer kontrolü yapılabilir.
1 2 3 4 5 |
public static void IsPattern(object o) { if (o is null) Console.WriteLine("Bu bir const patterndir"); if (o is 78) Console.WriteLine("Bu bir 78'dir :)"); } |
1 2 3 4 5 6 7 |
static void Main(string[] args) { foreach (var item in data) { IsPattern(item); } } |
Aşağıda, yukarıdaki data[ ] dizisindeki elemanların IsPattern() methodundaki kontrolünden sonraki çıktısı gösterilmiştir.
1 |
"dotnet run" |
- Type Pattern: Konturol edilecek nesnenin, istenen türle uyumlu olup olmadığına bakılır. Const pattern’inden tek farkı, eğer desen ilgili tip ile eşleşir ise nesne belirtilen türün yeni bir değişkenine atanır. Örneğin aşağıda sayısal olan tipler ekrana bulunup basılmıştır.
1 |
if (o is <strong><span style="color: #ff6600;">int i</span></strong>) Console.WriteLine($"Bu değer int tipine uymaktadır==> {i}"); |
Bir diğer örnek de Person sınıfına ait dizi elemanlarının isimlerinin ekrana basılmasıdır.
1 |
if (o is Person p) Console.WriteLine($"Bu kişi : {p.Name} dizi içerisinde bulunmaktadır."); |
Bu iki pattern’a ait ekran çıktısı aşağıdaki gibidir.
1 |
"dotnet run" |
Peki adı “Bo” ile başlıyan, Person sınıfına ait bir elemanı ilgili data [ ] dizisi içinde bulmak istese idik ne yapacaktık? Önce person tipine bakılıp, daha sonra && operator’ü ile istenen diğer koşullar aşağıdaki örnekde olduğu gibi peşi sıra eklenmelidir.
1 2 |
if (o is Person p && p.Name.StartsWith("Bo")) Console.WriteLine($"Adı Bo ile başlayan kişi ==> {p.Name} {p.Surname}'dir."); |
Var Pattern :
Diyelim ki, data [ ] dizisi içindeki tüm nesnelerin türü ekrana yazdırılmak isteniyor. Bakılacak tür için “var” anahtarı kullanıldığında, nesne her zaman bir türe sahip olduğu için, bu desen bize sadece true değerini döner. Ama esas amaç, nesnenin GetType() methodu ile hangi tipe ait olduğunun bulmak ve ekrana adını basmaktır. Aşağıdaki örnekde, null olan değerler için hataya düşülmemesi için soru ? işareti ile Not: “x?.GetType( )?.Name” şeklinde kullanılmıştır.
1 2 3 4 5 6 |
static int counter=0; public static void IsPattern(object o) { counter++; if (o is var x) Console.WriteLine($"{counter}. nesnenin türü {x?.GetType()?.Name}'dır."); } |
Pattern Matching’de Switch Statement Kullanılması :
Her zaman kullanılan switch ifadesi geliştirilerek, artık değişken tipine göre de ayırt edebilecek şekilde tanımlanmıştır. Ayrıca istenir ise, aranılan türün dışında “when” anahtar kelimesi de kullanılarak, aynı &&‘da olduğu gibi ekstra koşullar, sona doğru eklenerek arttırılabilir. Örneğin aşağıda “Person” sınıfına uyan ve Adı “Bo” ile başlayan şeklinde bir koşul kolaylıkla yaratılabilmiştir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public static void SwitchPattern(object o) { switch (o) { case null: Console.WriteLine("Gelen null bir değerdir."); break; case int i: Console.WriteLine("Integer tipinde bir sayıdır."); break; case Person p when p.Name.StartsWith("Bo"): Console.WriteLine($"Bo bir person {p.Name} dır."); break; case Person p: Console.WriteLine($"Başka bir person {p.Name} dır."); break; case var x: Console.WriteLine($"Sıradaki objenin [{x}] türü {x?.GetType().Name} dir "); break; default: break; } } |
Böylece eskiden aynı işi yapmak için yazdığımız kodlar “is“, “const“, “Type“, “var” ve “switch / case“ patternleri’i ile iyice kısalmaya başlamıştır. Aslında C# 7.0, daha en baştan bu özellikleri destekleyecek şekilde geliştirilmiştir. İlerde Sub-patterns, property patterns, positional patterns ve son olarak recursive patterns gibi daha nice patternlerin karşımıza çıkma ihtimali mevcuttur.
Olabilecek Patternler için Örnek Github: https://github.com/dotnet/csharplang/blob/master/proposals/patterns.md
Geldik bir makalenin daha sonuna. Yeni bir makalede görüşmek üzere hoşçakalın.
Source: https://github.com/dotnet/csharplang/tree/master/proposals
Source Code: https://github.com/borakasmer/PatternMatching
Elinize saglik hocam. Peki object tiplerin performans sorunu yarattigi konusundaki gorusunuz nedir? Baslangicta hangi tur olacagi bilinmiyor deniliyor. Boxing ve unboxing işlemlerinin yapılması gerektiğini biliyoruz objectlerde.
Aynı şekilde 4.0 ile gelen dynamic ile de kullanılabilir mi?
Öncelikle teşekkürler ve Selamlar Ali,
Dynamic ile bu konu biraz farklı. Hangi tipde olucağı bilinmeyen tipler her zaman bellekte yani remde çok yer kaplar. Objecttin de tipi belli değildir.
Ama Pattern Matching’de böyle bir durum yok. Gelen tipe göre farklı işler yapılıyor. Yani Boxing Unboxing yerine, arkada sadece GetType() işlemi var. Bu da performansdan ödün vermiyor.
İyi çalışmalar.