C# 7.0, Bu makalemizde C# 7.0 ile beraber gelecek yeniliklerden bahsedeceğim ve bu yeniliklerin her birini bir örnek üzerinden anlatmaya çalışacağım.
C# 7.0 İle Beraber Gelecek Yenilikler
Başlıkda ‘Gelecek’ diye bir kelime kullanılmasının nedeni henüz Release olarak sunulmuş değil bu özellikler ve Visual Studio 2017 ile beraber artık kodlarımız arasına girmesini bekliyoruz bu özelliklerin ama siz önceden test etmek isterseniz Visual Studio 2017 RC Community Edition sürümünü ücretsiz indirip deneyebilirsiniz.
C# 7.0 ile beraber gelen yenilikleri anlatırkan alttaki listeye göre örneklerle anlatmaya çalışacağım böylelikle örnekler üzerinden olayı daha iyi anlayacağız.
- Out Keyword
- Pattern Matching
- Switch Expression
- Local Functions
- Tuples
- Binary Literal
- Ref returns and locals
Out Keyword
C#’da out ile beraber kullandığımız parametrelerin şuan ki tanımı maalesef pek hoş değil nedenide öncelikle değişken tanımı yapıp daha sonra bu değişkeni out ile tanımlanmış parametreye vermeniz gerekiyor ama C# 7.0 ile beraber artık parametreye geçerken out tanımı yanında tipinide belirterek daha öncesinde bir değişken oluşturmak zorunda kalmayacağız böylelikle daha sade ve anlaşılır bir kod oluşturmuş olacağız. Şimdi bir örnek ile ne demek istediğimi görelim;
Varolan Yöntem
1 2 | int result; var isTrue = int.TryParse("1356565", out result); |
Yeni Yöntem
1 2 | var isTrue = int.TryParse("1356565", out int result); Console.WriteLine(result) |
Not: Üstte gördüğünüz tanımda WriteLine() kullandığımız satırda kapsamına girmemesine rağmen int.TryParse… adlı metod içerisinde yer alan out keyword’lü result adlı parametre sonrasındaki kod satırlarından ulaşılmasını sağlıyor.
Out keyword’u için bir başka örnek daha yapalım oda şöyle olacak parametre olarak geçeceğimiz string sayısal değeri TryParse… edip eğer sayısal bir değer ise girdiğimiz değer kadar * karakteri basılmasını sağlayacağız konsola.
1 2 3 4 5 6 | public void YildizlariYazdir(string s) { //Şuanki yöntem ile normalde burada int i; şeklinde bir tanımlama olması gerekirdi. if (int.TryParse(s, out var i)) { WriteLine(new string('*', i)); } else { WriteLine("Bulanık - yıldız yok bu gece!"); } } |
Not: Burada dikkat edilmesi gereken bir durum var i tanımını if bloğu arasında kullandığımız TryParse metodu içinde yapmış olsakta if bloğu dışındada kullanabiliyoruz.
Pattern Matching
Şimdi pattern matching için is ifadesi üzerinden örnek verelim.
1 2 3 4 5 6 7 8 9 | public void YildizlariYazdir(object o) { // C# 7.0 ile is ifadesinde null kontrol edilebiliyor. if (o is null) return; // is ifadesinde tip tanımı yanında eğer // o tipte ise değişken tanımıda yapılabiliyor. if (!(o is int i)) return; WriteLine(new string('*', i)); } |
Üstteki kodumuzda o is null tanımında o adlı değişkenin null olup olmadığını kontrol edebiliyor oluyoruz C# 7.0 ile beraber, ayrıca Out değişkenlerinde olduğu gibi is ifadesi sonrasında eğer tip eşleştirmesi sağlanırsa değişken tanımınında aynı kısımda yapılması hızlıca sağlanıyor böylelikle çok kısa ve anlaşılır bir kod ile yazabiliyor olacağız kodumuzu.
o is int i satırından sonra WriteLine içerisinde i değişkenini kullanabildiğimizide görüyorsunuz.
Şimdi Try… metodları ile beraber is ifadesini nasıl kullanabileceğimizi görelim.
1 2 3 4 | if (o is int i || (o is string s && int.TryParse(s, out i)) { /* i değişkenini kullanıyoruz */ } |
if’de yer alan ilk koşulumuzu bir üstteki kodumuzda gördük fakat ikinci koşulumuzda TryParse adlı metodun o is tring s şartında yer alan s değişkenine erişebildiğinide görüyoruz ve her iki koşuldada i adlı bir değişken oluşturuluyor ve bizde bu değişkeni blok içerisinde kullanabiliyoruz.
Switch Expressions
Bence C# 7.0 ile gelecek en güzel değişikliklerden biride switch-case için yapılmış değişikliklerdir. switch-case‘de kullanabildiğimiz tipler sınırlıydı ve sınıfları switch-case‘de kullanmak için taklalar atılması gerekiyordu ilk yenilik tamda burada geldi işte artık sınıfları swith ve case’de kullanabiliyor oluyoruz ve bu sınıfların özelliklerinede case blokları arasında erişebiliyoruz.
Diğer bir yenilikde case bloklarında when ile koşullar belirtebiliyoruz buda gerçekten çok başarılı bir değişiklik olmuş.
Son olarak case‘de null kontrölü yapılamıyordu artık case null: şeklinde bir kullanımda yapabiliyoruz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public class Shape { } public class Circle : Shape { public double Radius { get; set; } } public class Rectangle : Shape { public int Length { get; set; } public int Height { get; set; } } public void GetInfo(Shape shape) { switch (shape) { case Circle c: WriteLine($"circle with radius {c.Radius}"); break; case Rectangle s when (s.Length == s.Height): WriteLine($"{s.Length} x {s.Height} square"); break; case Rectangle r: WriteLine($"{r.Length} x {r.Height} rectangle"); break; default: WriteLine("<unknown shape>"); break; case null: throw new ArgumentNullException(nameof(shape)); } } |
Local Functions
Bu tam olarak şu anlama geliyor nested metod tanımları yapabiliyoruz class’larda olduğu gibi ama bir fark var nested class’larda iç içe girmiş classları access modifiers’ları izin verdiği sürece erişebiliyorduk ama iç içe girmiş metodlara dışarıdan erişme gibi bir durum yok tanımlı olduğu metod içerisinde kullanılabilmektedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private static void Main(string[] args) { //Main metodu içerisinde LocalFunction //adında bir local metod tanımı yaptık. //Metodun geri dönüş tipi int ve int //tipinde bir parametre alıyor. int LocalFunction(int arg) { return 41 * arg; } //LocalFunction adındaki metodumuza //1 değeri gönderiyoruz ve //bize 41 değerini dönderiyor. Console.WriteLine(LocalFunction(1)); } |
Tuples
Tuple yeni bir özellik değil sadece tanım daha basit hale getirilmiş şimdi bir örnek ile eski ve yeni kullanımı görelim.
Varolan Kullanım
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using System; using static System.Console; public class Program { public static void Main() { var item = GetItem(); WriteLine($"Ad: {item.Item1}"); WriteLine($"Doğum Yılı: {item.Item2}"); } static Tuple<string, int> GetItem() { var res = new Tuple<string, int>("Murat Öner", 1989); return res; } } |
Yeni Kullanım
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | using System; using static System.Console; public class Program { public static void Main() { var item = GetItem(); WriteLine($"Ad: {item.FullName}"); WriteLine($"Doğum Yılı: {item.Year}"); } static (string FullName, int Year) GetItem() => ("Murat Öner", 1989); } |
Binary Literal
Artık binary değer tanımlarını alttaki kodda gördüğünüz gibi yapabiliyoruz.
1 | int[] numbers = { 0b1, 0b100, 0b1000, 0b1_000, 0b10_0000 }; |
Ref Returns And Locals
Artık geri dönüş tipi tanımlarında değişken tanımlarında ve metod kullanımında ref anahtar kelimesini kullanabiliyoruz. Böylelikle referans olarak işaretlediğimiz değerler üzerinde yaptığımız değişiklikler referans olarak işaretlenmiş ve kullanılmış her yerde otomatik değişecektir bu int değer olur, array olur veya list nesneleri olur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public ref int Find(int number, int[] numbers) { for (int i = 0; i < numbers.Length; i++) { if (numbers[i] == number) { return ref numbers[i]; // return the storage location, not the value } } throw new IndexOutOfRangeException($"{nameof(number)} not found"); } int[] array = { 1, 15, -39, 0, 7, 14, -12 }; ref int place = ref Find(7, array); // aliases 7's place in the array place = 9; // replaces 7 with 9 in the array WriteLine(array[4]); // prints 9 |
Tüm kodlar
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | using System; using static System.Console; namespace ConsoleApp1 { public class Program { static void Main(string[] args) { #region Binary Literal int[] numbers = { 0b1, 0b100, 0b1000, 0b1_000, 0b10_0000 }; #endregion #region Pattern Matching //WriteStars(null); //WriteStars("abc"); //WriteStars("12"); #endregion #region Ref Returns And Locals int[] array = { 1, 15, -39, 0, 7, 14, -12 }; ref int place = ref Find(7, array); // aliases 7's place in the array place = 9; // replaces 7 with 9 in the array WriteLine(array[4]); // prints 9 #endregion #region Switch Case Features //GetInfo(new Circle()); //GetInfo(new Rectangle { Height = 50, Length = 50 } ); //GetInfo(new Rectangle { Height = 25, Length = 50 } ); #endregion #region Tuples var res = GetItem(); WriteLine($"Ad: {res.FullName}"); WriteLine($"Doğum Yılı: {res.Year}"); #endregion } #region Pattern Matching public static void WriteStars(object o) { if (o is null) return; if (!(o is int i)) return; WriteLine(new string('*', i)); } #endregion #region Ref Returns And Locals public static ref int Find(int number, int[] numbers) { for (int i = 0; i < numbers.Length; i++) { if (numbers[i] == number) { return ref numbers[i]; // return the storage location, not the value } } throw new IndexOutOfRangeException($"{nameof(number)} not found"); } #endregion #region Swith Case Features public class Shape { } public class Circle : Shape { public double Radius { get; set; } } public class Rectangle : Shape { public int Length { get; set; } public int Height { get; set; } } public static void GetInfo(Shape shape) { switch (shape) { case Circle c: WriteLine($"circle with radius {c.Radius}"); break; case Rectangle s when (s.Length == s.Height): WriteLine($"{s.Length} x {s.Height} square"); break; case Rectangle r: WriteLine($"{r.Length} x {r.Height} rectangle"); break; default: WriteLine("<unknown shape>"); break; case null: throw new ArgumentNullException(nameof(shape)); } } #endregion #region Tuples static (string FullName, int Year) GetItem() => ("Murat ÖNER", 1989); #endregion } } |
[fa class=”fa-github”] Şimdiye kadar anlattıklarımızı ve diğer C# 7. 0 örnekleri için buraya tıklayıp github repository’isinden erişebilirsiniz.
Bu özelliklerin bazılarını Mads Torgersen anlatımıyla izlemek isterseniz alttaki videoyu seyredebilirsiniz.