↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 3 — Konu 12: `switch` / `switch expression` (Modern Dart)

4 dk okuma #dart
Dizi · 12/64 Dart Türkçe Tutorial
  1. Bölüm 1 — Konu 1: Dart Nedir, Nerede Kullanılır, Neden Flutter Bu Dili Seçti
  2. Bölüm 1 — Konu 2: Ortam Kurulumu (Dart SDK, DartPad, Terminal ile Çalıştırma)
  3. Bölüm 1 — Konu 3: İlk Program (`main()`, `print()`, Dosya Yapısı)
  4. Bölüm 1 — Konu 4: Yorum Satırları, Temel Sözdizimi Kuralları
  5. Bölüm 2 — Konu 5: Değişken Tanımlama — `var`, `final`, `const` Farkı
  6. Bölüm 2 — Konu 6: Temel Tipler — `int`, `double`, `String`, `bool`
  7. Bölüm 2 — Konu 7: Tip Çıkarımı (Type Inference) ve Açık Tip Belirtme
  8. Bölüm 2 — Konu 8: `dynamic` ve `Object` — Ne Zaman Kullanılır, Ne Zaman Kaçınılır
  9. Bölüm 2 — Konu 9: Null Safety Temelleri (`?`, `!`, `late`'e Giriş)
  10. Bölüm 3 — Konu 10: Aritmetik, Atama, Karşılaştırma, Mantıksal Operatörler
  11. Bölüm 3 — Konu 11: `if / else if / else`
  12. Bölüm 3 — Konu 12: `switch` / `switch expression` (Modern Dart)
  13. Bölüm 3 — Konu 13: Ternary Operatör, `??`, `??=`, `?.`
  14. Bölüm 3 — Konu 14: `for`, `while`, `do-while` Döngüleri
  15. Bölüm 4 — Konu 16: `List` — Oluşturma, Erişim, Temel Metodlar
  16. Bölüm 4 — Konu 17: `Set` — Benzersiz Eleman Mantığı
  17. Bölüm 4 — Konu 18: `Map` — Key-Value Yapılar
  18. Bölüm 4 — Konu 19: Koleksiyon Üzerinde `for-in`, `forEach`
  19. Bölüm 4 — Konu 20: Spread Operatörü (`...`, `...?`) ve Collection If/For
  20. Bölüm 5 — Konu 21: Fonksiyon Tanımlama, Parametreler (Positional, Named, Optional)
  21. Bölüm 5 — Konu 22: Varsayılan Parametre Değerleri (Derinlemesine)
  22. Bölüm 5 — Konu 23: Arrow Function (`=>`) Sözdizimi
  23. Bölüm 5 — Konu 24: Fonksiyonlar Birinci Sınıf Vatandaş — Değişkene Atama, Parametre Olarak Geçme
  24. Bölüm 5 — Konu 25: Anonim Fonksiyonlar ve Closure Kavramı (Derinlemesine)
  25. Bölüm 5 — Konu 26: Recursion (Özyineleme)
  26. Bölüm 6 — Konu 27: Class Tanımlama, Constructor (Varsayılan, Named, Factory)
  27. Bölüm 6 — Konu 28: Alanlar (Fields), Metodlar, `this` Kullanımı (Derinlemesine)
  28. Bölüm 6 — Konu 29: Initializer List, Constructor Kısayolları (Derinlemesine)
  29. Bölüm 6 — Konu 30: Getter / Setter
  30. Bölüm 6 — Konu 31: Statik Üyeler (`static`)
  31. Bölüm 7 — Konu 32: Kalıtım (`extends`), `super` Kullanımı
  32. Bölüm 7 — Konu 33: Metod Override Etme, `@override`
  33. Bölüm 7 — Konu 34: Soyut Sınıflar (`abstract class`)
  34. Bölüm 7 — Konu 35: Interface Mantığı (`implements`)
  35. Bölüm 7 — Konu 36: Mixin (`with`)
  36. Bölüm 7 — Konu 37: `enum` — Basit ve Gelişmiş (Metotlu Enum'lar)
  37. Bölüm 8 — Konu 38: `try / catch / finally`, `throw`
  38. Bölüm 8 — Konu 39: Özel Exception Sınıfları Yazma
  39. Bölüm 8 — Konu 40: Sound Null Safety Derinlemesine — `late`, `required`, Non-Nullable Varsayılan Davranış
  40. Bölüm 8 — Konu 41: `assert` ile Geliştirme Zamanı Kontrolleri
  41. Bölüm 9 — Konu 42: Generic Sınıflar ve Fonksiyonlar
  42. Bölüm 9 — Konu 43: Generic Sınırlamalar (`<T extends ...>`)
  43. Bölüm 9 — Konu 44: Dart'ın Built-in Generic Koleksiyonları Nasıl Çalışır
  44. Bölüm 10 — Konu 45: `Future` Kavramı, `async`/`await`
  45. Bölüm 10 — Konu 46: `Future.then`, Hata Yönetimi (`catchError`)
  46. Bölüm 10 — Konu 47: `Stream` Temelleri, `async*`, `yield`
  47. Bölüm 10 — Konu 48: `StreamController`, Broadcast Stream
  48. Bölüm 11 — Konu 49: `map`, `where`, `reduce`, `fold`
  49. Bölüm 11 — Konu 50: `sort`, Custom Comparator ile Sıralama
  50. Bölüm 11 — Konu 51: Iterable Kavramı Derinlemesine (Lazy Evaluation)
  51. Bölüm 11 — Konu 52: Cascade Notasyonu (`..`)
  52. Bölüm 12 — Konu 53: `pubspec.yaml`, pub.dev'den Paket Ekleme
  53. Bölüm 12 — Konu 54: Kütüphane Oluşturma, `import`/`export`, `part`/`part of`
  54. Bölüm 12 — Konu 55: Dart'ta Test Yazımı (`test` Paketi, Unit Test Mantığı)
  55. Bölüm 12 — Konu 56: Extension Methods
  56. Bölüm 12 — Konu 57: Dart'ın Derleme Modelleri (JIT vs AOT) ve Bunun Flutter'a Etkisi
  57. Bölüm 13 — Konu 59: Dart 3 Pattern Matching — Records, Destructuring, Sealed Classes, Exhaustive Switch
  58. Bölüm 13 — Konu 60: `Never` Tipi, `covariant` ve Generic Variance Detayları
  59. Bölüm 13 — Konu 61: Event Loop Derinlemesine — Microtask Queue vs Event Queue
  60. Bölüm 13 — Konu 62: Memory Model & Performans — Garbage Collection, `const` Nesnelerin Bellek Avantajı
  61. Bölüm 13 — Konu 63: Isolate'ler Arası Mesajlaşmanın Maliyeti ve `compute()`'un İç İşleyişi
  62. Bölüm 13 — Konu 64: FFI (Foreign Function Interface) — C Koduna Erişim
  63. Bölüm 13 — Konu 65: Sunucu Tarafında Dart — `dart:io`, `shelf` Paketiyle Basit Bir Backend/CLI Aracı Yazma
  64. Bölüm 13 — Konu 66: Derleyici & Analiz Araçları — `dart analyze`, Custom Lint Kuralları, `build_runner` Mimarisi
Dizinin sayfası →
İçindekiler 9 başlık
  1. Neden switch Var? if/else if Yetmiyor mu?
  2. Klasik switch İfadesi (Statement)
  3. break Neden Zorunlu? Dart'ta Fall-Through Aslında Yok
  4. Dart 3 — switch Expression (İfade Olarak Switch)
  5. Karşılaştırma — Ne Zaman Hangisi?
  6. Exhaustiveness (Kapsayıcılık) Kontrolü — Enum'larla Küçük Bir Önizleme
  7. Sayılarla switch Kullanımı
  8. 🎯 Bu Dersten Çıkarılması Gerekenler
  9. 📝 Ödevler

Neden switch Var? if/else if Yetmiyor mu?

Teknik olarak her switch bir if/else if zinciriyle de yazılabilir. Ama tek bir değeri birden çok sabit değerle karşılaştıracağın durumlarda switch, hem daha okunabilir hem de (Dart 3 ile) derleyicinin eksik durumları sana haber vermesini sağlayan bir yapıdır.

Klasik switch İfadesi (Statement)

dart
void main() {
  String gun = "Pazartesi";

  switch (gun) {
    case "Pazartesi":
      print("Haftanın ilk günü");
      break;
    case "Cumartesi":
    case "Pazar":
      print("Haftasonu");
      break;
    default:
      print("Normal bir gün");
  }
}

Yapıyı parçalayalım:

  • switch (gun) — karşılaştırılacak değeri belirtir.
  • case "Pazartesi": — değer bu case ile eşleşirse, altındaki kod çalışır.
  • break; — bu case'in bittiğini belirtir.
  • case "Cumartesi": case "Pazar": — birden fazla case, aralarında hiç kod olmadan art arda yazılırsa, aynı bloğu paylaşırlar (bu case'lerden herhangi biri eşleşirse aynı kod çalışır). Bu, aşağıda anlatılacak fall-through kuralının tek istisnasıdır.
  • default: — hiçbir case eşleşmezse çalışacak yedek blok (else gibi düşünebilirsin).

break Neden Zorunlu? Dart'ta Fall-Through Aslında Yok

C, Java veya JavaScript gibi dillere aşinaysan muhtemelen "fall-through" kavramını biliyorsundur: o dillerde bir case'in sonuna break koymazsan kontrol sessizce bir sonraki case'e "düşer" ve bu sık karşılaşılan bir hata kaynağıdır.

Dart bu davranışı baştan itibaren (bu bir Dart 3 değişikliği değil) engeller. Dart'ta, içinde kod bulunan (boş olmayan) bir case bloğu, break, continue, return veya throw gibi bir "çıkış" ifadesi olmadan bitiyorsa, bu bir derleme zamanı hatasıdır — kod hiç çalışmaz, hiç derlenmez. Yani aşağıdaki kod:

dart
void main() {
  int sayi = 1;

  switch (sayi) {
    case 1:
      print("Bir");
      // break yok!
    case 2:
      print("İki");
      break;
    default:
      print("Diğer");
  }
}

derlenmez. Dart analizörü şuna benzer bir hata verir:

metin
Error: The 'case' should end with a statement like 'break', 'continue', 'return' or 'throw'.
Try adding one of the statements before the next 'case'.

Yani "önce yanlışlıkla iki satır yazdırılır, sonra derleyici bunu fark edip uyarır" gibi bir şey olmuyor — kod derlenme aşamasında reddediliyor, hiç çalışma şansı bulamıyor. Bu, Dart'ın C/Java tarzı dillerin en klasik hata kaynaklarından birini tasarım gereği ortadan kaldırdığı anlamına gelir: unutulan bir break senin projende sessiz bir bug olarak değil, derleyicinin anında durdurduğu bir hata olarak karşına çıkar.

Peki hiç fall-through yapamaz mısın? Yapabilirsin — ama bunun için kasıtlı ve açık olman gerekir. Dart, continue anahtar kelimesini bir case etiketiyle birlikte kullanmana izin verir:

dart
void main() {
  int sayi = 1;

  switch (sayi) {
    case 1:
      print("Bir");
      continue iki; // açıkça "case iki'ye devam et" diyoruz
    iki:
    case 2:
      print("İki");
      break;
    default:
      print("Diğer");
  }
}

Çıktı:

metin
Bir
İki

Burada iki: bir etiket (label), case 2'nin hemen üstüne konmuş. continue iki; ifadesi derleyiciye "buradan case 2'ye bilerek geçiyorum, bu bir hata değil" diyor. Bu özellik nadiren kullanılır ama var olduğunu bilmen yeterli — pratikte çoğu zaman aynı işi case "Cumartesi": case "Pazar": örneğindeki gibi boş case birleştirme ile ya da fonksiyona çıkarma ile daha okunabilir şekilde çözersin.

Dart 3 — switch Expression (İfade Olarak Switch)

Dart 3 ile gelen en kullanışlı yeniliklerden biri: switch'i bir ifade (expression) olarak kullanabilmek, yani doğrudan bir değer üretip değişkene atayabilmek:

dart
void main() {
  String gun = "Pazartesi";

  String mesaj = switch (gun) {
    "Pazartesi" => "Haftanın ilk günü",
    "Cumartesi" || "Pazar" => "Haftasonu",
    _ => "Normal bir gün",
  };

  print(mesaj);
}

Klasik switch statement ile farkları:

  • case kelimesi yok, doğrudan değer yazılır.
  • : yerine => kullanılır (arrow function'a benzer sözdizimi).
  • break gerekmez — her kol zaten tek bir değer üretir, fall-through riski yoktur.
  • Birden fazla değer için || kullanılır (case "Cumartesi": case "Pazar": yerine "Cumartesi" || "Pazar" =>).
  • default yerine _ (alt çizgi, "her şeyi eşleştir" anlamında) kullanılır.
  • Sonunda virgülle biten her kol, bir sonraki koldan virgülle ayrılır.

Bu yeni sözdizimi, özellikle bir değer üretmek istediğinde (klasik switch'te olduğu gibi ayrı bir değişken tanımlayıp her case'de ona atama yapmak yerine) çok daha temiz bir kod sağlar.

Karşılaştırma — Ne Zaman Hangisi?

dart
// Klasik switch statement — YAN ETKİ üretmek için (print, API çağrısı vb.)
void gunBilgisiYazdir(String gun) {
  switch (gun) {
    case "Pazartesi":
      print("Toplantı günü");
      break;
    default:
      print("Normal gün");
  }
}

// switch expression — DEĞER üretmek için
String gunTipi(String gun) {
  return switch (gun) {
    "Cumartesi" || "Pazar" => "Haftasonu",
    _ => "Haftaiçi",
  };
}

Genel kural: Eğer amacın bir değer üretip döndürmek ise (fonksiyondan return etmek, bir değişkene atamak), switch expression kullan — daha kısa ve güvenlidir. Eğer amacın birkaç farklı yan etki (side effect — print, API çağrısı, state değiştirme vb.) tetiklemekse, klasik switch statement daha uygun olabilir.

Exhaustiveness (Kapsayıcılık) Kontrolü — Enum'larla Küçük Bir Önizleme

Enum'ları henüz detaylı işlemedik (Bölüm 7'de göreceğiz) ama switch'in en güçlü yanlarından biri, enum'larla kullanıldığında ortaya çıkıyor:

dart
enum TrafikIsigi { kirmizi, sari, yesil }

String eylemBelirle(TrafikIsigi isik) {
  return switch (isik) {
    TrafikIsigi.kirmizi => "Dur",
    TrafikIsigi.sari => "Hazırlan",
    TrafikIsigi.yesil => "Geç",
  };
}

Burada default (_) yazmadık — ve Dart buna izin veriyor çünkü TrafikIsigi enum'unun tüm olası değerlerini (kirmizi, sari, yesil) kapsadık. Eğer enum'a yeni bir değer eklenirse (örneğin TrafikIsigi.turuncu), Dart derleyicisi bu switch ifadesinin artık eksik (non-exhaustive) olduğunu sana derleme zamanında söyler — böylece unutkanlıktan kaynaklanan hataları baştan önler. Bu özelliği Dart 3'ün sealed class yapılarıyla birlikte Bölüm 13'te çok daha derinlemesine işleyeceğiz; bu sadece bir önizlemeydi.

Sayılarla switch Kullanımı

dart
void main() {
  int puan = 85;

  String harfNotu = switch (puan) {
    >= 90 => "AA",
    >= 80 => "BA",
    >= 70 => "BB",
    >= 60 => "CB",
    _ => "FF",
  };

  print(harfNotu); // BA
}

Dikkat et — burada case değerleri artık sabit değil, >= 90 gibi bir karşılaştırma ifadesi. Bu da Dart 3'ün switch expression'a kazandırdığı güçlü bir özellik (pattern matching'in bir parçası) — klasik switch statement'ta bu mümkün değildi, sadece sabit değerlerle eşleştirme yapılabilirdi.


🎯 Bu Dersten Çıkarılması Gerekenler

  • Klasik switch statement: içinde kod olan her case, break (veya continue/return/throw) ile bitmek zorundadır — bunu atlamak Dart'ta çalışma zamanı hatası değil, derleme zamanı hatasıdır. Bu, Dart 3'e özgü değil, dilin en başından beri var olan bir kuraldır.
  • Sessiz fall-through Dart'ta yoktur; kasıtlı fall-through istersen continue etiketliCase; sözdizimini açıkça kullanman gerekir.
  • Art arda gelen tamamen boş case'ler (case "Cumartesi": case "Pazar":) aynı bloğu paylaşabilir — bu fall-through değil, birden fazla case'i tek bir sonuca bağlamanın kasıtlı bir yoludur.
  • Dart 3 switch expression: => ile değer üretir, _ ile varsayılan durum, || ile çoklu eşleşme.
  • Değer üretmek için switch expression, yan etki tetiklemek için klasik switch statement tercih edilir.
  • Enum'larla kullanıldığında, tüm değerler kapsandığında default gerekmez ve derleyici eksik durumları yakalar (exhaustiveness).
  • switch expression'da karşılaştırma ifadeleri (>= 90 gibi) de kullanılabilir.

📝 Ödevler

  • [ ] Bir haftanın günlerini ("Pazartesi"'den "Pazar"'a) alıp haftaiçi/haftasonu ayrımı yapan hem klasik switch statement hem de switch expression versiyonunu yaz, ikisini karşılaştır.
  • [ ] Bilerek bir case'in sonuna break koymadan derlemeyi dene ve derleyicinin sana verdiği hata mesajını oku — ardından continue etiketliCase; sözdizimiyle kasıtlı bir fall-through örneği yaz.
  • [ ] Bir notu (0-100) alıp harf notuna çeviren switch expression'ı, karşılaştırma ifadeleriyle (>= 90 gibi) yaz.
  • [ ] Basit bir enum (örn. mevsimler: ilkbahar, yaz, sonbahar, kis) tanımla, her mevsim için bir mesaj döndüren exhaustive bir switch expression yaz — default kullanmadan.

Sıradaki konu: Bölüm 3 — Konu 13: Ternary Operatör, ??, ??=, ?.