C# Konu başlıkları
A'dan Z' ye C# Eğitimi
1.1 C# Nedir? .NET Framework / .NET Core Nedir?
C# (okunuşu C Sharp), Microsoft'un geliştirdiği sihirli bir dildir. Bu dil, bilgisayarınıza ne yapması gerektiğini söylemenizi sağlar. Tıpkı bir aşçının tarifi okuması gibi, bilgisayar da C# kodunu okur ve talimatları uygular.
⭐ C# Ne İşe Yarar? (Benzetme: Süper Güçleri)
- Oyun Yapımı: Özellikle Unity ile harika 3D oyunlar yapabilirsiniz.
- Web Siteleri: İnternette gördüğünüz Facebook, Instagram gibi dinamik sayfaların arka planını (sunucu kısmını) yapabilirsiniz.
- Masaüstü Programları: Bilgisayarınızda çalışan Word, Excel gibi uygulamaları yapabilirsiniz.
🚀 .NET Platformu: C# Kodunun Çalıştığı Saha
C# bir dil ise, .NET bu dilin konuşulduğu ve çalıştığı büyük bir platformdur. Tıpkı bir tiyatro sahnesi gibi düşünebilirsiniz:
- .NET Framework (Eski Sahne): Sadece Windows'ta oynatılabilen eski tiyatro sahnesi.
- .NET Core / .NET (Yeni, Taşınabilir Sahne): Windows, Mac ve Linux gibi her yerde (Cross-platform) çalışabilen modern ve hızlı tiyatro sahnesi. Artık yeni projelerde hep bu modern sahne kullanılır.
1.2 C# Programının Yapısı (Main(), using, namespace, class)
Bir C# programı, tıpkı bir ev gibi belli bölümlerden oluşur. Her bölümün bir görevi vardır.
🏡 Yapı Taşları ve Görevleri (Benzetme: Evdeki Odalar)
-
using System;(Malzeme Çantası): "System" ismindeki kütüphaneden (hazır araç kutusundan) malzemeleri alacağımı söylüyorum. Bu sayedeConsole.WriteLinegibi komutları kullanabilirim. -
namespace(Mahalle/Proje Adı): Kodlarımızın karışmaması için bir isim alanı (mahalle) tanımlarız. Uygulamanın adı genellikle namespace olur. -
class(Ev): Programdaki temel yapı birimidir. İçinde metotlar (odalar) barındırır. -
Main()(Giriş Kapısı): Bilgisayarın programı çalıştırmaya başladığı ilk yerdir. C# 9+ ile bu zorunluluk kalksa da, klasik yapıyı anlamak önemlidir.
Klasik Program Yapısı:
using System; // 1. Gerekli kütüphaneleri çağır
namespace BenimIlkProjem // 2. Mahallemizin adı
{
class Program // 3. Ana Evimiz
{
static void Main(string[] args) // 4. Çalışmaya başlama noktası
{
Console.WriteLine("Merhaba Dünya!"); // İşte komutumuz!
}
}
}
1.3 Derleyici ve Çalışma Mantığı (CLR, IL, JIT)
Bilgisayarlar, bizim yazdığımız C# dilini doğrudan anlamaz. Tıpkı farklı diller konuşan iki kişi gibi, bir çevirmene ihtiyaç vardır. Bu çeviri işlemi iki aşamada gerçekleşir:
1. Aşama: Derleme (Compile Time) - C#'tan Ara Dile
Siz C# kodunu yazarsınız. Derleyici (Compiler), bu kodu alır ve İngilizce benzeri bir ara dile (bilgisayar için daha kolay) çevirir. Bu ara dilin adı IL (Intermediate Language)'dir.
- Sonuç: Kodunuz, `.exe` dosyası içindeki IL koduna dönüşür. Bu kod hala direkt çalışmaz.
2. Aşama: Çalıştırma (Run Time) - Sihirli Kutu
Programı çalıştırdığınız anda devreye **CLR (Common Language Runtime)** adlı bir sihirli kutu girer.
- CLR'nin Görevi: IL kodunu okur ve o anda kullanılan işletim sisteminin (Windows, Mac, Linux) anlayacağı Makine Koduna dönüştürür.
- JIT (Just-In-Time) Compiler: CLR'nin içindeki bir araçtır. Tam zamanında (çalışma anında) kodu makine diline çevirir. Bu sayede C# her yerde çalışabilir!
- Garbage Collector (Çöp Toplayıcı): CLR'nin en önemli süper gücüdür. Bizim kullanmayı bitirdiğimiz gereksiz verileri (bellekteki çöpü) otomatik olarak temizler. Geliştirici olarak belleği elle temizlemek zorunda kalmayız.
1.4 Değişkenler ve Veri Türleri
Değişkenler, bilgisayarın hafızasında geçici olarak bilgi sakladığı kutulardır. Her kutunun içine ne tür bir şey koyacağınızı (sayı, yazı, evet/hayır) önceden belirtmeniz gerekir. Buna **Veri Tipi** denir. C# bu konuda çok titizdir (Strongly-Typed).
📦 Temel Veri Tipleri (Kutu Çeşitleri)
| Veri Tipi | Ne Saklar? | Örnek (Kullanım) | Benzetme |
|---|---|---|---|
| int | Tam Sayılar (1, 100, -5) | int yas = 15; |
Sadece tam elmaların sığdığı kutu. |
| double | Ondalıklı Sayılar (3.14, 0.5) | double fiyat = 49.99; |
Para veya ağırlık gibi ondalıklı değerlerin kutusu. |
| char | Tek bir karakter ('a', '7', '$') | char ilkHarf = 'A'; |
Sadece tek bir harf kağıdının sığdığı küçük kutu. |
| string | Uzun Yazılar/Cümleler ("Merhaba C#") | string ad = "Ali Veli"; |
İçine roman sığdırabileceğiniz büyük bir sandık. |
| bool | Sadece true (doğru) veya **false (yanlış)** | bool islemTamam = true; |
İçine sadece "Evet" veya "Hayır" yazan bir kart sığar. |
✨ var Anahtar Kelimesi (Akıllı Kutu)
Eğer değişkenin değerini hemen veriyorsanız, C# 9+ ile gelen var anahtar kelimesini kullanabilirsiniz. C# tipi sizin için otomatik olarak bulur. Bu, programın hala katı tipli (Strongly-Typed) olduğu gerçeğini değiştirmez, sadece yazmayı kolaylaştırır.
var sayi = 25; // C# bunu otomatik olarak int yapar.
1.5 Tür Dönüşümleri (Implicit / Explicit Casting)
Bazen bir kutudaki bilgiyi alıp başka bir kutuya (başka bir veri tipine) koymak isteriz. İşte burada Tür Dönüşümü (Casting) devreye girer. Bu, iki şekilde yapılır:
1. Implicit (Örtülü) Dönüşüm: Güvenli Geçiş (Küçükten Büyüğe)
Küçük bir kutudaki bilgiyi büyük bir kutuya koymak gibi. Veri kaybı riski yoktur ve C# bunu otomatik olarak yapar.
Örnek: Bir tam sayıyı (int) ondalıklı sayıya (double) çevirmek.
int tamSayi = 10; // 4 baytlık küçük kutu
double ondalikliSayi = tamSayi; // 8 baytlık büyük kutuya sorunsuz sığar. (10.0 olur)
2. Explicit (Açık) Dönüşüm: Riskli Geçiş (Büyükten Küçüğe)
Büyük bir kutudaki bilgiyi zorla küçük bir kutuya sığdırmak gibi. Veri kaybı riski **vardır** (ondalıklı kısmın kaybolması gibi). Bu yüzden C# sizden parantez içinde hedef tipi belirtmenizi zorunlu tutar.
Örnek: Bir ondalıklı sayıyı (double) tam sayıya (int) çevirmek.
double piSayisi = 3.14; // Ondalıklı kısım var.
int tamKisim = (int)piSayisi; // (int) diyerek "Sorumluluğu alıyorum" deriz. Sonuç: 3.
Ek: String Dönüşümü
Sayılardan veya diğer tiplerden yazıya (string) dönüşüm yapmak için genellikle .ToString() metodu kullanılır. Bu, bir bilgiyi ekranda göstermek için en yaygın yoldur.
int yas = 20; string yazi = yas.ToString(); // "20" olur.
1.6 Sabitler (const, readonly)
Değişkenler, adı üstünde, değeri değişebilen kutulardır. Sabitler ise değeri hiçbir zaman değişmeyecek özel kutulardır. Tıpkı anayasal bir kural gibi, program çalışırken kimse bu değeri değiştiremez. Sabitler, kodun okunabilirliğini ve güvenliğini artırır.
📌 const (Kalıp Sabit)
Değeri, program derlenirken (yazılırken) kesinleşir ve asla değiştirilemez. En yaygın sabit türüdür.
- Kullanım: Evrensel, bilinen, değişmez değerler için (Pi sayısı, vergiler, maksimum deneme sayısı).
- Örnek:
public const double PI_SAYISI = 3.14159;
🔒 readonly (Kurucu Sabiti)
Değeri, sınıfın ilk oluşturulduğu anda (constructor içinde) atanabilir, daha sonra değiştirilemez. Değeri programın çalışmasına bağlı olabilir (örneğin bir ayar dosyasından okunan değer).
- Kullanım: Program çalışmaya başladığında belirlenen, ancak sonradan değiştirilmesi istenmeyen değerler için (Uygulama Versiyonu, Kurulum Tarihi).
- Örnek:
public readonly string UygulamaID = Guid.NewGuid().ToString();
Farkın Anahtarı:
const: Değerini kod yazılırken bilmelisiniz.
readonly: Değerini kod çalışmaya başlarken belirleyebilirsiniz.
1.7 Operatörler
Operatörler, değişkenler ve değerler üzerinde matematiksel veya mantıksal işlemler yapmamızı sağlayan özel sembollerdir. Tıpkı bir hesap makinesindeki tuşlar gibi.
➕ Aritmetik Operatörler (Matematik)
| Operatör | Açıklama | Örnek | Sonuç |
|---|---|---|---|
+ | Toplama | 5 + 3 | 8 |
- | Çıkarma | 10 - 4 | 6 |
* | Çarpma | 2 * 6 | 12 |
/ | Bölme (Tam sayı bölmesi) | 10 / 3 | 3 (Ondalık kısmı atar!) |
% | Modül (Kalan) | 10 % 3 | 1 (10'un 3'e bölümünden kalan) |
++ / -- | Arttırma / Azaltma (1 ekle/çıkar) | sayi++ | Sayının değerini 1 arttırır. |
⚖️ Karşılaştırma Operatörleri (Karar Verme)
Bunlar sadece doğru (true) veya yanlış (false) değeri döndürür.
| Operatör | Açıklama | Örnek | Sonuç |
|---|---|---|---|
== | Eşit mi? | 5 == 5 | true |
!= | Eşit değil mi? | 5 != 6 | true |
> / < | Büyük / Küçük | 10 > 7 | true |
>= / <= | Büyük eşit / Küçük eşit | 5 >= 5 | true |
🔌 Mantıksal Operatörler (Birden Çok Koşul)
Birden fazla koşulu birleştirmek için kullanılır.
| Operatör | Açıklama | Örnek |
|---|---|---|
&& | VE (İki koşul da doğruysa true) | (yaş > 18) && (ehliyetVar == true) |
|| | VEYA (Koşullardan biri doğruysa true) | (haftaIci) || (erkenSaat) |
! | DEĞİL (Doğruyu yanlışa, yanlışı doğruya çevirir) | !acikMi |
➡️ Atama ve Birleştirme Operatörleri
- Basit Atama:
=(int x = 10;) - Kısa Yollar:
+=(x += 5;, yanix = x + 5;demektir) - Ternary (Koşullu) Operatör:
koşul ? değer_doğru : değer_yanlış
string durum = (saat > 12) ? "Öğleden Sonra" : "Öğleden Önce";
1.8 if, else if, else (Karar Verme Ağacı)
Programlamada en temel karar verme yapısıdır. Tıpkı bir trafik lambası gibi, bir koşul doğruysa (yeşil ışık) bir yol izlenir; yanlışsa (kırmızı ışık) başka bir yol izlenir.
🚦 if (Eğer)
Belirtilen koşul doğru ise içindeki kod bloğu çalışır.
if (havaGunesli == true)
{
Console.WriteLine("Dışarı çık!");
}
🛑 else (Değilse)
if koşulu yanlış ise, otomatik olarak else içindeki kod çalışır.
if (havaGunesli)
{ /* ... */ }
else
{
Console.WriteLine("Evde kal!");
}
🟡 else if (Aksi Halde Eğer)
Birbiri ardına birden fazla koşulu kontrol etmek için kullanılır. İlk doğru koşul bulunduğunda diğerleri kontrol edilmez.
int puan = 75;
if (puan >= 90) {
Console.WriteLine("AA");
} else if (puan >= 70) { // Buraya sadece 70 ile 89 arasındakiler gelir.
Console.WriteLine("BB");
} else {
Console.WriteLine("FF");
}
Önemli: Basit bir if bloğunda sadece tek bir satır varsa küme parantezleri ({}) isteğe bağlı olarak kullanılmayabilir, ancak karışıklığı önlemek için kullanılması önerilir.
1.9 switch – case (Çoklu Seçenek Kutusu)
Eğer aynı değişkenin birden fazla olası değerini kontrol etmeniz gerekiyorsa, uzun bir if-else if zinciri yerine switch-case kullanmak kodu daha okunaklı yapar. Tıpkı bir ATM menüsü gibi, bir seçeneği seçersiniz.
⚙️ Klasik switch Yapısı
Her case bloğu bittikten sonra mutlaka break; komutu kullanılmalıdır (aksi halde hata verir).
int gunNo = 3;
switch (gunNo)
{
case 1:
Console.WriteLine("Pazartesi");
break;
case 5:
case 6: // Birden fazla case aynı anda çalışabilir
Console.WriteLine("Haftasonu");
break;
default: // Hiçbiri eşleşmezse (else gibi)
Console.WriteLine("Geçersiz Gün");
break;
}
⚡ Modern C# Switch İfadeleri (C# 8+)
Daha kısa ve bir değer döndürebilen modern yapıdır. Daha çok yeni projelerde tercih edilir.
string renk = "Mavi";
string kategori = renk switch
{
"Kırmızı" => "Sıcak",
"Mavi" => "Soğuk",
_ => "Nötr" // _ (alt çizgi), default'un yerini alır.
};
1.10 Döngüler (Tekrar Eden Görevler)
Döngüler, aynı kod bloğunu art arda, defalarca çalıştırmak için kullanılır. Tıpkı bir şarkıyı baştan sona tekrar tekrar dinlemek gibi.
1. for (Sayılı Tekrar)
Ne kadar tekrar edeceğinizi **başlangıçta** biliyorsanız en uygun döngüdür.
// 0'dan başla, 5'e kadar (5 dahil değil), her seferinde 1 arttır
for (int i = 0; i < 5; i++)
{
Console.WriteLine(i); // Çıktı: 0, 1, 2, 3, 4
}
2. while (Koşul Doğru Olduğu Sürece)
Ne kadar süreceği belli olmayan, sadece bir koşulun doğruluğuna bağlı olan tekrarlar için.
int sayac = 0;
while (sayac < 3) // Koşul yanlış olana kadar çalış
{
Console.WriteLine("Çalışıyor...");
sayac++;
}
3. do-while (En Az Bir Kez Çalışma Garantisi)
Koşulu kontrol etmeden **önce** en az bir kere çalışır, sonra koşulu kontrol eder ve devam eder.
int x = 100;
do
{
Console.WriteLine("Bir kez çalıştım.");
} while (x < 10); // Koşul yanlış olsa bile 1 kez çalıştı.
4. foreach (Koleksiyon Gezgini)
Dizilerdeki (Arrays) veya Listelerdeki (Collections) **tüm** elemanları tek tek gezmek için en güvenli ve okunaklı yoldur. İndekslerle uğraşmak zorunda kalmazsınız.
string[] meyveler = { "Elma", "Armut", "Muz" };
foreach (string meyve in meyveler)
{
Console.WriteLine(meyve); // Çıktı: Elma, Armut, Muz
}
1.11 break, continue, return (Akış Kontrol Anahtarları)
Bu üç anahtar kelime, programın normal akışını değiştirmek için kullanılır. Tıpkı bir koşu sırasında aniden durmak, bir turu atlamak veya yarışı bitirmek gibi.
🛑 break (Anında Dur, Dışarı Çık)
İçinde bulunduğu döngüyü veya switch bloğunu **hemen sonlandırır** ve program akışını o bloğun dışına atar.
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // i=5 olduğunda döngü biter (5 yazılmaz).
}
Console.WriteLine(i); // Çıktı: 0, 1, 2, 3, 4
}
↪️ continue (Bu Turu Atla, Devam Et)
Döngünün o anki turunu atlar ve döngüyü bir sonraki tura geçirir. Döngü tamamen sonlanmaz.
for (int i = 0; i < 5; i++) {
if (i == 2) {
continue; // i=2 olduğunda atlanır (2 yazılmaz).
}
Console.WriteLine(i); // Çıktı: 0, 1, 3, 4
}
🔙 return (Metodu Bitir ve Geri Dön)
İçinde bulunduğu metodu tamamen bitirir ve program akışını metodun çağrıldığı yere geri götürür. Eğer metot bir değer döndürüyorsa, değeri de yanında taşır.
public int Topla(int a, int b)
{
return a + b; // Metot biter ve sonucu çağrıldığı yere gönderir.
}
1.12 Tek ve Çok Boyutlu Diziler (Sıralı Kutular)
Diziler (Arrays), aynı veri tipindeki **sabit sayıda** veriyi sırayla saklayan büyük kutulardır. Dizilerdeki her elemana bir **indeks (sıra numarası)** ile ulaşılır. **Önemli: C#'ta indeksler 0'dan başlar!**
📏 Tek Boyutlu Diziler (Sıra Halindeki Kutular)
En basit dizi tipidir. Tek bir satır halinde veriyi tutar.
// 5 elemanlı bir int dizisi oluştur
int[] puanlar = new int[5];
puanlar[0] = 95; // İlk eleman (0. indeks)
puanlar[4] = 70; // Son eleman (4. indeks)
// Tanımlarken değer verme
string[] gunler = { "Pzt", "Sal", "Çar" };
🖼️ Çok Boyutlu Diziler (Tablo veya Matris)
Veriyi satırlar ve sütunlar halinde tutar. En yaygını iki boyutlu dizilerdir (matrisler).
Çokgen Diziler (Rectangular Array): Her satırın aynı sayıda sütunu vardır (düzenli bir tablo).
// 2 satır, 3 sütunluk bir matris
int[,] matris = new int[2, 3];
matris[0, 1] = 5; // 0. Satır, 1. Sütun
Jagged Diziler (Düzensiz Diziler): Her satırın farklı sayıda sütunu olabilir (düzensiz bir yapı).
// Satır sayısı 3, sütunlar sonra belirlenir.
int[][] jaggedDizi = new int[3][];
jaggedDizi[0] = new int[] { 1, 2 }; // İlk satır 2 elemanlı
jaggedDizi[1] = new int[] { 3, 4, 5, 6 }; // İkinci satır 4 elemanlı
1.13 Array Sınıfı Kullanımı
C#'ta oluşturduğumuz tüm diziler (int[], string[] vb.), System.Array isminde büyük bir sınıftan miras alır. Bu sınıf, dizilerle iş yapmayı kolaylaştıran bir sürü **hazır araç (metot)** içerir.
🛠️ Array Sınıfının Güçlü Metotları
-
Array.Sort(dizi): Dizideki elemanları küçükten büyüğe (veya A'dan Z'ye) sıralar. -
Array.Reverse(dizi): Dizinin sırasını tamamen tersine çevirir. -
Array.IndexOf(dizi, eleman): Belirtilen elemanın dizideki ilk sırasını (indeksini) bulur. Bulamazsa -1 döndürür. -
Array.Copy(kaynak, hedef, uzunluk): Bir dizideki elemanları başka bir diziye kopyalar.
Örnek Kullanım:
int[] sayilar = { 40, 10, 30, 20 };
Array.Sort(sayilar); // Dizi şimdi {10, 20, 30, 40}
int indeks = Array.IndexOf(sayilar, 30); // Çıktı: 2
Array.Length Özelliği: Dizinin kaç elemanı olduğunu öğrenmek için kullanılır.
int uzunluk = sayilar.Length; // Çıktı: 4
1.14 List, Dictionary, Queue, Stack Temel Kullanımı
Diziler sabit boyutluydu, ama gerçek hayatta eleman ekleyip çıkarmamız gerekir. Koleksiyonlar, bu dinamik ihtiyacı karşılar. Bu seviyede genellikle sadece temel dinamik listelerden bahsedilir.
1. List (Sürekli Büyüyebilen Dinamik Dizi)
En çok kullanılan koleksiyondur. Dizilerin tüm özelliklerine sahiptir, ancak boyutu otomatik olarak büyür/küçülür. (Bu seviyede sadece tip güvenliği olmayan (non-generic) ArrayList yerine modern List<T>'ye giriş yapılır, ama detayları Orta Seviye'ye bırakılır).
- Ekleme:
isimler.Add("Ahmet"); - Silme:
isimler.Remove("Ahmet"); - Örnek (List):**
List<string> isimler = new List<string>();
2. Dictionary (Sözlük Kutusu - Anahtar/Değer)
Verileri bir anahtar (key) ve bu anahtara karşılık gelen bir değer (value) olarak saklar. Tıpkı bir sözlükte kelimeyi (anahtar) arayıp anlamını (değer) bulmak gibi.
- Ekleme:
iller.Add(34, "İstanbul"); - Erişim:
string sehir = iller[34]; // "İstanbul" - Örnek (Dictionary):
Dictionary<int, string> iller = new Dictionary<int, string>();
3. Queue (Sıra - İlk Giren İlk Çıkar: FIFO)
Tıpkı marketteki kuyruk gibi çalışır. **İlk giren eleman, ilk çıkar.**
- **Ekleme:**
musteriKuyrugu.Enqueue("Ali"); - **Çıkarma:**
string ilkMusteri = musteriKuyrugu.Dequeue(); // Ali çıkar
4. Stack (Yığın - Son Giren İlk Çıkar: LIFO)
Tıpkı üst üste dizilmiş tabaklar gibi çalışır. Son konulan tabak, ilk alınır. Geri alma (Undo) işlemleri için idealdir.
- Ekleme:
tabaklar.Push("Mavi Tabak"); - Çıkarma:
string usttekiTabak = tabaklar.Pop(); // Mavi Tabak çıkar
2.1 Sınıflar, Nesneler ve Metotlar
Orta seviye, programlamanın süper kahramanlar ligi olan Nesne Yönelimli Programlama (Object-Oriented Programming - OOP) ile başlar. OOP, gerçek hayattaki varlıkları (arabalar, kediler, insanlar) kod içinde modellememizi sağlar.
🧱 Sınıf (Class): Fabrika Kalıbı
Bir **sınıf**, sadece bir plan, bir taslaktır. Tıpkı bir araba yapmak için gereken çizimler gibi. Kendi başına çalışmaz, ama ondan sonsuz sayıda parça (nesne) üretilebilir.
Sınıflar iki ana şey içerir:
- Alanlar (Fields) / Özellikler (Properties): Varlığın sahip olduğu şeyler (Arabanın Rengi, Kapı Sayısı). Bunlar veridir.
- Metotlar (Methods): Varlığın yapabildiği eylemler (Arabanın Çalışması, Hızlanması). Bunlar fonksiyondur.
public class Araba // Bu bir sınıf (kalıp) adı
{
public string Renk; // Özellik (Veri)
public int MaksHiz; // Özellik (Veri)
public void Calistir() // Metot (Eylem)
{
Console.WriteLine("Motor çalıştı, Araba kullanıma hazır!");
}
}
🚗 Nesne (Object): Üretilmiş Gerçek Parça
Bir nesne ise, bu sınıftan üretilmiş, hafızada yer kaplayan somut bir örnektir. Tıpkı o çizimlerden üretilmiş gerçek bir mavi araba gibi.
// 'new' anahtar kelimesi ile sınıftan nesne üretilir.
Araba benimAracim = new Araba(); // Nesne oluşturuldu
benimAracim.Renk = "Mavi"; // Özelliğe değer atama
benimAracim.MaksHiz = 200;
benimAracim.Calistir(); // Nesnenin metodunu çağırma
2.2 Özellikler (Properties) ve Erişim Belirleyiciler
OOP'nin dört temel sütunundan biri olan **Kapsülleme (Encapsulation)**, veriyi dışarıdan gelecek doğrudan müdahalelerden koruma sanatıdır. Bunu **Özellikler (Properties)** ve **Erişim Belirleyiciler (Access Modifiers)** ile yaparız.
🔒 Erişim Belirleyiciler: Kim Nereye Bakabilir?
Bunlar, bir sınıfın, metodun veya alanın kimler tarafından kullanılabileceğini belirleyen anahtarlardır. (Detaylar 1.7'de de vardı, burada OOP bağlamında tekrar vurgulanır.)
- public: Her yerden erişilebilir. (Açık Mektup)
- private: Sadece tanımlandığı sınıf içinden erişilebilir. (Özel Günlük)
- protected: Tanımlandığı sınıf ve ondan miras alan sınıflar erişebilir. (Aile Sırrı)
🔑 Özellikler (Properties): Güvenli Geçitler (get/set)
Alanlara (Field) direkt erişimi engelleriz (**private** yaparak) ve Properties (Özellikler) kullanarak güvenli bir kapı açarız. Bu sayede atama (set) veya okuma (get) sırasında kurallar ekleyebiliriz.
Klasik Property Yapısı (Kapsülleme):
private int _yas; // Veri, dışarıdan gizlendi (private)
public int Yas // Özellik (Güvenli Geçit)
{
get { return _yas; } // Değeri güvenle oku
set // Değeri atarken kural koy
{
if (value >= 18) { _yas = value; } // 18'den küçükse atama yapma!
}
}
Auto-Implemented Properties (Otomatik Özellikler):
Kurala ihtiyacınız yoksa C# kısa bir yol sunar:
public string Adi { get; set; } // C# arka planda private alanı otomatik oluşturur.
2.3 Kurucular (constructor) ve this, Statik üyeler
🔨 Kurucular (Constructors): Nesnenin Doğumu
Bir sınıfın nesnesi new anahtar kelimesi ile oluşturulduğu anda çalışan özel metottur. Görevi, nesneyi kullanıma hazırlamak ve başlangıç değerlerini atamaktır.
- Varsayılan Kurucu: Parametre almayandır. (Siz yazmasanız bile C# otomatik ekler.)
- Parametreli Kurucu: Nesneye başlangıçta değer atamak için kullanılır.
Örnek:
public class Ogrenci
{
public string Ad { get; set; }
public Ogrenci(string adGelen) // Kurucu Metot
{
Ad = adGelen; // Nesne oluşurken Ad özelliğini ayarla
}
}
👤 this Anahtar Kelimesi: Kendime Ait
Bazen metot içinde tanımladığınız değişken adı ile sınıfın içindeki alan adı aynı olabilir. this. kullanımı, mevcut nesnenin alanına veya metoduna işaret ettiğini gösterir.
public Ogrenci(string ad) // ad parametresi ile Ad alanı karışabilir
{
this.Ad = ad; // this.Ad sınıfın alanı, ad ise parametredir.
}
🏛️ Statik (static) Üyeler: Sınıfa Ait Ortak Alan
Normal üyeler (metotlar, alanlar) **nesneye** aitken, static üyeler **sınıfın kendisine** aittir. Bir nesne oluşturmadan doğrudan sınıf adı üzerinden erişilir ve tüm nesneler bu tek, ortak değeri paylaşır.
- **Örnek:**
Math.PI(Pi sayısı) veya bir sayaç.
public static int OgrenciSayisi = 0; // Ortak sayac
// Kullanım:
Ogrenci.OgrenciSayisi++; // Nesne oluşturmadan çağrıldı.
2.4 Kalıtım (Inheritance) ve base
Kalıtım, bir sınıfın (türetilmiş/alt sınıf) başka bir sınıftan (temel/üst sınıf) tüm özellik ve metotlarını miras almasıdır. Bu, kod tekrarını önler ve "is-a" (bir ...'dır) ilişkisini modellememizi sağlar. **OOP'nin Yeniden Kullanım (Reusability) prensibi.**
Genel Kalıtım Şeması (is-a İlişkisi)
- Temel Sınıf (Base Class): Hayvan (Genel özellikler burada tanımlanır).
- Türetilmiş Sınıf (Derived Class): Köpek (Hayvan'ın tüm özelliklerini alır ve kendi özel özelliklerini ekler).
Örnek:
public class Hayvan // Temel Sınıf
{
public string Ad { get; set; }
public void YemekYe() { Console.WriteLine("Yemek yiyor."); }
}
public class Kopek : Hayvan // Kalıtım sembolü (Köpek bir Hayvan'dır)
{
public void Havla() { Console.WriteLine("Hav hav!"); }
}
Artık Kopek nesnesi de YemekYe() metodunu kullanabilir.
⬆️ base Anahtar Kelimesi
base, türetilmiş bir sınıf içinden, hemen üstündeki temel sınıfa ait üyelere (metotlar, kurucular) erişmek için kullanılır. Özellikle temel sınıfın kurucusunu çağırmak için önemlidir.
public Kopek(string ad) : base(ad) // Base'in kurucusunu çağır
{
// Kopek'e özel başlangıç kodları
}
2.5 Metot Geçersiz Kılma (override, virtual, new)
Kalıtım yoluyla alınan bir metot, alt sınıfta farklı bir iş yapacaksa, onu yeniden tanımlamamız gerekir. Bu olaya Metot Geçersiz Kılma (Method Overriding) denir.
🔄 virtual ve override (Dinamik Değişim)
Bu, Kalıtım ve Polimorfizmin anahtarıdır.
- virtual: Temel sınıfta, bu metodun alt sınıflar tarafından değiştirilebilir (geçersiz kılınabilir) olduğunu belirtir. (Bu metot opsiyoneldir.)
- override: Alt sınıfta, temel sınıftaki
virtualmetodu yeniden tanımladığını belirtir.
Örnek (Ses Çıkarma):
public class Hayvan
{
public virtual void SesCikar() { Console.WriteLine("Tanımsız Ses"); } // Opsiyonel
}
public class Kedi : Hayvan
{
public override void SesCikar() { Console.WriteLine("Miyav!"); } // Yeniden yazıldı
}
🆕 new (Metot Gizleme)
Eğer temel sınıftaki metot virtual değilse, onu gizlemek için new anahtar kelimesi kullanılır. Bu, OOP kuralı olan "override" değildir; sadece temel sınıftaki metot ile aynı isimde yeni bir metot oluşturur (Gizleme, tavsiye edilmez).
Fark: virtual/override gerçek bir OOP değişimini (Polimorfizm) sağlar. new sadece aynı isimli bir metodu gizler.
2.6 Polimorfizm (Çok Biçimlilik)
Polimorfizm (Polymorphism), bir nesnenin birden fazla tür gibi davranabilmesi veya bir eylemin (metodun) farklı nesnelerde farklı sonuçlar vermesidir. **Tek bir arayüz, çoklu sonuç.**
1. Statik Polimorfizm (Compile Time Polymorphism)
Program daha derlenirken hangi metodun çalışacağı belirlenir. Bu, Metot Aşırı Yükleme (Method Overloading) ile yapılır.
- Metot Overloading: Aynı isimde ama farklı parametre sayısına veya tipine sahip metotlar tanımlamaktır.
public int Topla(int a, int b) { return a + b; } // 2 parametre
public double Topla(double a, double b) { return a + b; } // Farklı tipler
public int Topla(int a, int b, int c) { return a + b + c; } // 3 parametre
2. Dinamik Polimorfizm (Run Time Polymorphism)
Hangi metodun çalışacağı, program çalışırken (run time) belirlenir. Bu, Metot Geçersiz Kılma (Overriding) ile yapılır. (2.5 konusundaki virtual ve override kullanımı.)
Uygulama Örneği:
Bir Hayvan listesine hem Kedi hem de Kopek ekleyebiliriz. Döngüde SesCikar() metodunu çağırdığımızda, C# nesnenin asıl tipine bakarak doğru metodu çalıştırır.
Hayvan hayvan1 = new Kedi();
Hayvan hayvan2 = new Kopek();
// Tek bir komut:
hayvan1.SesCikar(); // Miyav!
hayvan2.SesCikar(); // Hav hav!
2.7 Abstract Sınıflar (Soyutlama)
Soyutlama (Abstraction), OOP'nin bir diğer sütunudur. Kullanıcıya sadece gerekli bilgileri gösterip karmaşık detayları gizlemeyi amaçlar. Abstract sınıflar, bu soyutlamayı sağlamanın bir yoludur.
🧭 Abstract Class: Yarım Kalmış Taslak
- Nesnesi oluşturulamaz: Abstract sınıflar, "bir şeyin" tam olarak ne olduğunu belirtmediği için
new AbstractSinif()ile nesne oluşturulamaz. (Soyut bir Hayvan'ın ayak izi olmaz.) - Miras alınması ZORUNLUDUR: Sadece temel bir yapı (taslak) sunar.
- Normal ve Abstract metotları içerebilir.
🚫 Abstract Metot: Boşluk Doldurma Zorunluluğu
abstract anahtar kelimesiyle tanımlanan metotların gövdesi (içindeki kod) yoktur. Alt sınıflar bu metodu mutlaka kendi içlerinde (override ile) tanımlamak zorundadırlar. (Tıpkı bir formu doldurmak gibi.)
Örnek:
public abstract class Sekil // Nesnesi oluşturulamaz
{
public abstract double AlanHesapla(); // Alt sınıflar bunu yapmak zorunda!
public void Yazdir() { /* ... */ } // Normal metot da olabilir.
}
public class Kare : Sekil
{
public override double AlanHesapla() { /* Kareye özgü hesaplama */ return 0; }
}
2.8 Arayüzler (interface) ve Çoklu Arayüz Kullanımı
**Arayüzler (Interfaces)**, bir sınıfın hangi yeteneklere (metotlara) sahip olması gerektiğini belirleyen bir taahhüt listesi veya sözleşmedir. Abstract sınıflardan daha katı bir şablondur.
📜 Interface: Sözleşme Listesi
- Sadece metot imzaları (Adı, parametreleri) bulunur, gövdesi (içindeki kod) yoktur.
- Tüm üyeleri örtülü olarak public ve abstract'tır.
- Tıpkı bir formu doldurmak gibi, arayüzü uygulayan sınıf tüm metotları yapmak zorundadır.
- C#'ta sınıflar sadece tek bir sınıftan miras alabilir, ancak birden çok arayüzü (Multi-Inheritance) uygulayabilir.
Çoklu Yetenek Sağlama (Multiple Inheritance of Interfaces)
Bir sınıf, birden fazla arayüzü uygulayarak o arayüzlerin gerektirdiği tüm metotları yapmak zorundadır. Bu, bir nesneye birden fazla rol (yetenek) verme imkanı tanır.
Örnek:
public interface IUcabilir { void Uc(); } // Uçma yeteneği
public interface IYuzebilir { void Yuz(); } // Yüzme yeteneği
public class Ordek : IUcabilir, IYuzebilir // İki sözleşmeyi de uyguladı
{
public void Uc() { Console.WriteLine("Ördek uçuyor."); }
public void Yuz() { Console.WriteLine("Ördek yüzüyor."); }
}
Arayüzler, özellikle İleri Seviye (DI, Repository) konularında büyük önem taşır.
2.9 struct, enum ve record tipleri (C# 9+)
🧊 struct (Yapılar): Değer Tipi Sınıflar
Sınıflar (class) referans tipi (Reference Type) iken, struct (yapı) değer tipidir (Value Type). Küçük veri yapıları için kullanılır ve bellekte yığın (Stack) bölgesinde tutularak daha hızlı çalışabilir. (Örnek: int veya DateTime).
- Temel Fark: struct kopyalandığında, verinin kendisi kopyalanır (sınıflarda ise sadece referans kopyalanır).
🚦 enum (Numaralandırmalar): Sabit Seçenek Kümeleri
Bir değişkenin alabileceği değerleri sabit bir küme ile sınırlandırmak için kullanılır. Bu, kodu daha okunaklı ve hatasız yapar. (Örn: Haftanın günleri, renkler, durum kodları).
public enum Renkler
{
Kirmizi,
Yesil,
Mavi // Aslında Kirmizi=0, Yesil=1, Mavi=2 demektir.
}
Renkler secilen = Renkler.Mavi;
Immutable Veri Modelleri: record (C# 9+)
Record tipi, özellikle veri transferi (DTO - Data Transfer Object) veya basit, değişmez (Immutable) veri modelleri için tasarlanmış modern bir referans tipidir (struct gibi değer tipi de olabilir, ama default referans tipidir).
- Süper Gücü: Otomatik olarak tüm özellikleri karşılaştıran
Equals()metodu ve kurucuyu (constructor) oluşturur.
// Tek satırda veri sınıfı tanımı:
public record Musteri(int Id, string Ad);
Musteri m1 = new Musteri(1, "Ali");
// m1.Ad = "Veli"; // Hata verir, çünkü recordlar değişmez (immutable).
3.1 Generic Koleksiyonlar (List<T>, Dictionary<T> vs.)
Temel seviyede gördüğünüz List veya Dictionary'nin modern ve performanslı versiyonudur. **Generic** yapılar, veri tipini belirleyerek (<T>) tip güvenliğini artırır ve kutulama (boxing) ihtiyacını ortadan kaldırarak performansı iyileştirir.
✨ Generic Kavramı (Tipi Belirtilen Kutu)
Generic yapılar, kodu yazarken hangi tipte veri tutacağını bilmeden (T ile temsil ederek) bir sınıf veya metot tasarlamanızı sağlar. Tıpkı "bir şey" tutacak ama ne tutacağı sonradan belli olacak bir konteyner gibi.
- List<T>: Sadece tek tip (T) veri tutan, dinamik boyutlu dizi. (Örn: Sadece String tutan List<string>)
- Dictionary<TKey, TValue>: Belirtilen tipte anahtar (TKey) ve belirtilen tipte değer (TValue) tutar.
Avantajı: Tip Güvenliği. Derleyici, yanlış tipte veri eklenmesini engeller. Bu sayede çalışma zamanı hataları (Runtime Errors) azalır.
List<int> notlar = new List<int>();
notlar.Add(85);
// notlar.Add("Yüz"); // Hata verir, çünkü sadece int alabilir.
3.2 IEnumerable, ICollection, IList Arayüzleri
Bu arayüzler, C#'taki koleksiyonların omurgasını oluşturur. Bir koleksiyonun sahip olabileceği yetenekleri (okuma, sayma, ekleme) soyut bir şekilde tanımlar.
📜 IEnumerable (En Temel Sözleşme: Sadece Gezinme)
Bir koleksiyonun üzerinde foreach döngüsü ile gezinebileceğinizi (okuyabileceğinizi) garanti eder. Sadece okuma ve üzerinde döngü kurma yeteneği sağlar. Bu, en az yetenekli, ancak en güvenli arayüzdür.
- Kullanım: LINQ (3.7) sorgularının başlangıç noktasıdır. Metotlardan veri döndürülürken genellikle bu tip tercih edilir (Verinin değiştirilmesini engellemek için).
⛓️ ICollection (Sayma ve Ekleme/Silme Temelleri)
IEnumerable'a ek olarak, koleksiyonun eleman sayısını (Count), eleman ekleme (Add) ve silme (Remove) yeteneklerini ekler. Genellikle koleksiyonların temel yönetimini tanımlar.
📋 IList (İndeksli Erişim: Konum Bilgisi)
ICollection'a ek olarak, en zengin arayüzdür. Verilere **indeks numarasıyla** erişim yeteneği ([i]) ve verileri belirli bir konuma ekleme (Insert) yeteneği ekler. List<T> bu arayüzü uygular.
Özet Akış:
IList > ICollection > IEnumerable
3.3 try, catch, finally ve throw (Hata Yönetimi)
Programın beklenen bir hatayla karşılaştığında (örneğin: 0'a bölme, olmayan bir dosyayı okuma, veritabanı bağlantı hatası) çökmesini engellemek için İstisna Yönetimi (Exception Handling) kullanılır.
Saf Programlama Akışı
- try: Denemek istediğimiz, hata çıkma ihtimali olan kod bloğu bu kısma yazılır. (Güvenli Alan)
- catch: Eğer
trybloğunda bir hata (istisna) oluşursa, kontrol hemencatchbloğuna geçer. Burada hatayı yakalarız. - finally: Hata çıksın ya da çıkmasın, mutlaka çalıştırılması gereken kod bloğudur. (Örn: Veritabanı bağlantısını kapatma, dosyayı serbest bırakma).
try
{
int sonuc = 10 / 0; // Hata burada fırlatılır
}
catch (DivideByZeroException ex) // Sadece 0'a bölme hatasını yakala
{
Console.WriteLine("Hata oluştu: " + ex.Message);
}
finally
{
Console.WriteLine("İşlem sonlandı.");
}
💥 throw: Hata Fırlatma
Bir programcı olarak, belirli bir koşul oluştuğunda bilerek bir hata (istisna) oluşturmak ve bunu yakalanması için üst katmana fırlatmak için throw kullanılır.
if (miktar < 0)
{
throw new ArgumentException("Miktar negatif olamaz!");
}
3.4 Özel İstisna Sınıfları (Custom Exception)
C# birçok hazır istisna (IOException, NullReferenceException) sunar, ancak iş mantığınıza özel, daha anlamlı hatalar yaratmak için kendi istisna sınıfınızı yazmanız gerekir.
Kullanım Amacı: Anlam Yüklemek
Bir hata mesajı yerine, "Bakiye Yetersiz" veya "Kullanıcı Kayıtlı Değil" gibi anlamlı bir istisna fırlatarak, bu istisnayı yakalayan üst katmanın (örneğin Web API) daha net kararlar vermesini sağlarsınız (Örn: 400 Bad Request döndürmek).
Kurallar:
- Yeni istisna sınıfı, mutlaka
System.Exceptionsınıfından miras almalıdır. - Adı genellikle
Exceptionile bitmelidir (Örn:BakiyeYetersizException).
Örnek:
// 1. Kendi Hata Sınıfımız
public class YetersizBakiyeException : Exception
{
public YetersizBakiyeException(string mesaj) : base(mesaj) { }
}
// 2. Kullanım
if (hesap.Bakiye < miktar)
{
throw new YetersizBakiyeException("Çekmek istediğiniz miktar bakiyenizden yüksek.");
}
3.5 Delegate ve Event Kavramları
🤝 Delegate: Metotlar İçin Bir Pointer (Temsilci)
Delegate'ler, C#'ta metotlara referans veren tip güvenli işaretçilerdir. Tıpkı bir "Metot Sözcüsü" gibi, hangi metotların çağrılacağını kararlaştıran bir tiptir. Olay tabanlı (Event-Driven) programlamanın temelini oluşturur.
- Tanım: Bir metot imzası (dönüş tipi ve parametreleri) tanımlar.
- Kullanım: Metodu doğrudan çağırmak yerine, delegate üzerinden çağırırsınız. Bu, çalışma zamanında hangi metodun çalışacağına karar vermenizi sağlar (Callback mekanizması).
public delegate void EkranaYazdirici(string mesaj); // Delegate tanımı
public void NormalYazdir(string m) { /*...*/ }
EkranaYazdirici temsilci = NormalYazdir; // Metodu temsilciye atama
temsilci("Merhaba"); // Temsilci üzerinden çağırma
📢 Event (Olay): Yayınlama Mekanizması
Event'ler, bir nesnenin (Yayıncı), başka bir nesneye (Abone) bir şey olduğunu güvenli bir şekilde bildirmesini sağlar. Delegate'ler event'leri tanımlamak için kullanılır.
- Kullanım: Bir düğmeye basıldığında, veritabanı değiştiğinde veya süre dolduğunda ilgili diğer nesnelerin bilgilendirilmesi.
- event anahtar kelimesi: Abone olan sınıfların olayı sadece başlatmasına değil, sadece abone olmasına izin verir, böylece güvenlik sağlanır.
public event EkranaYazdirici DurumDegisti;
3.6 Action, Func, Predicate ve Lambda İfadeleri
C#'ın bu modern özellikleri, delegate kullanımını sadeleştirir ve kod tekrarını azaltır. Genellikle LINQ ve asenkron programlamada kullanılır.
🌟 Action, Func, Predicate (Hazır Delegate Tipleri)
Kendi delegate tipinizi tanımlamak yerine kullanabileceğiniz yerleşik tiplerdir:
- Action: Dönüş değeri olmayan (
void) metotlar için. (Max 16 parametre alır.)
Action<string> yazdir = (m) => Console.WriteLine(m); - Func: Dönüş değeri olan metotlar için. (Son tip dönüş tipidir.)
Func<int, int, int> topla = (a, b) => a + b; // İki int al, bir int döndür - Predicate: Dönüş değeri mutlaka bool olan (koşul kontrolü) metotlar için.
Predicate<int> ciftMi = (sayi) => sayi % 2 == 0;
➡️ Lambda İfadeleri: Kısa Metotlar
Anonim (isimsiz) metotlar oluşturmanın ve delegate'lere atamanın kısa yoludur. => (Goes to / Gider) operatörü kullanılır.
(parametreler) => { gövde/işlem }
Bu, özellikle LINQ sorgularında veriyi filtrelerken, sıralarken veya dönüştürürken çok sık kullanılır.
3.7 LINQ'e Giriş (Language Integrated Query)
**LINQ**, C# diline entegre edilmiş bir sorgulama dilidir. Amacı, ister bellek içindeki koleksiyon (List), ister veritabanı (SQL), ister XML veya JSON olsun, tüm veri kaynaklarını **aynı C# sözdizimiyle** sorgulamanızı sağlamaktır.
SQL'den Esinlenen Sözdizimi
LINQ, SQL'e benzeyen, okunaklı bir sözdizimi sunar. (Query Syntax)
- where: Veriyi filtreler. (Koşula uyanı al.)
- select: Verinin şeklini değiştirir, hangi alanların alınacağını belirler.
- orderby: Veriyi sıralar.
- group by: Veriyi ortak bir özelliğe göre gruplar.
Örnek:
var buyukSayilar = from s in sayilar // sayilar koleksiyonu üzerinde
where s > 50 // koşul koy
orderby s descending // büyükten küçüğe sırala
select s; // sonuçları seç
3.8 Anonymous Type ve Lambda ile LINQ Sorguları
LINQ'in sorgu sözdiziminden (Query Syntax) daha popüler olanı, Metot Sözdizimi (Method Syntax)'dir. Bu sözdizimi, Lambda İfadeleri (3.6) ve Anonymous Type (Anonim Tipler) ile birlikte kullanılır.
➡️ Lambda ile Metot Sözdizimi
Lambda ifadeleri, where, select gibi metotların içine kolayca koşul yazmamızı sağlar.
// Aynı sorgu, metot sözdizimi ile:
var buyukSayilar = sayilar.Where(s => s > 50).OrderByDescending(s => s).ToList(); // Daha kısa, daha akıcı
👓 Anonymous Type (Anonim Tipler)
Sorgu sonucunda, yeni bir sınıf oluşturmaya gerek duymadan anlık, geçici bir veri yapısı oluşturmak için kullanılır. Tipi yoktur, sadece o anlık değerleri tutar.
var sonuclar = ogrenciler.Where(o => o.Ortalama > 80)
.Select(o => new { // Anonim tip oluştur
Isim = o.Ad,
GecmeNotu = o.Ortalama
});
Bu, veriyi ihtiyacınız olan minimum alanlarla hızlıca dönüştürmenizi sağlar.
3.9 Asenkron Programlama (async ve await)
Geleneksel programlama (senkron), bir iş bitmeden diğerine geçmez. (Örn: Veritabanından veri okumayı bekler.) Asenkron programlama ise, bu bekleme süresini (G/Ç işlemleri: I/O Bound) boşa harcamamak, o sırada başka işler yapmak demektir.
⌛ async ve await (Söz Ver ve Bekle)
- async: Bir metodun asenkron çalışabileceğini, içinde
awaitkullanılabileceğini belirtir. (Bu metot bir söz veriyor.) - await: Asenkron bir görevin (Task) tamamlanmasını bekler, ancak bu sırada ana iş parçacığını (thread) engellemez. (Beklerken başka işe geçer.)
Benzetme:
Senkron: Fırında kek pişiriyorsunuz, kek pişene kadar fırının başında bekliyorsunuz.
Asenkron: Keki fırına koyup, alarmı kuruyor, bu sırada bulaşık yıkıyorsunuz. Alarm çalınca geri gelip keki alıyorsunuz.
Örnek:
public async Task<List<Urun>> VeriOkuAsync()
{
Console.WriteLine("Veri okuma başladı.");
var veri = await dbContext.Urunler.ToListAsync(); // Veritabanı işlemini beklerken kontrolü CLR'ye bırak.
Console.WriteLine("Veri okuma bitti.");
return veri;
}
3.10 Task, ThreadPool ve Paralel İşlemler
Asenkron programlamanın temel taşıdır. Arka planda çalışan işleri temsil ederler.
Task (Asenkron İşin Temsilcisi)
Task, bir işlemin (metot) gelecekte tamamlanacağının sözünü veren bir nesnedir. Bir görev başladığında hemen bir Task döndürülür ve bu nesne, görevin tamamlanma durumunu ve sonucunu (Task<TResult>) taşır.
ThreadPool (İş Parçacığı Havuzu)
Tüm Task'ler, CLR tarafından yönetilen bir iş parçacığı havuzu (ThreadPool) tarafından yürütülür. CLR, bu havuzdaki hazır ve optimize edilmiş küçük iş parçacıklarını kullanarak gereksiz kaynak yaratımını engeller.
Paralel İşlemler (CPU Bound)
Asenkron (async/await), I/O (Giriş/Çıkış) beklemelerini yönetirken, Paralel Programlama (Parallel.For, Parallel.ForEach) CPU yoğun işlemleri aynı anda birden fazla çekirdekte çalıştırmak için kullanılır. Bu, gerçekten eşzamanlı çalışmadır ve daha çok işlemci gücü gerektirir.
List<int> sayilar = Enumerable.Range(1, 1000).ToList();
Parallel.ForEach(sayilar, sayi =>
{
// Bu işlem birden fazla çekirdekte aynı anda yapılır.
});
3.11 System.IO ve Dosya Okuma / Yazma
Bu alan, işletim sistemi üzerindeki dosyalar ve klasörlerle etkileşim kurmanızı sağlar. **System.IO** namespace'i kullanılır.
Temel Sınıflar ve Görevleri:
- File ve Directory: Dosya ve dizin yaratma, silme, taşıma gibi statik işlemler.
- FileStream: Bir dosya üzerindeki veriyi (byte olarak) okumak veya yazmak için düşük seviyeli erişim sağlar.
- StreamReader / StreamWriter: Dosyayı metin olarak (String) okumak veya yazmak için kullanılır (daha yüksek seviyeli ve kolay).
💧 using Deyimi (IDisposable)
Dosya işlemleri veya veritabanı bağlantıları gibi kaynaklar kullanıldıktan sonra mutlaka serbest bırakılmalıdır. using anahtar kelimesi, nesnenin işi bittiğinde otomatik olarak Dispose() metodunu çağırarak kaynağı temizler. (Kaynak sızıntısını engeller).
using (StreamWriter yazar = new StreamWriter("log.txt"))
{
yazar.WriteLine("Uygulama başladı.");
} // using bloğu bitince dosya otomatik kapanır.
3.12 JSON / XML Serileştirme
Serileştirme (Serialization), C# nesnelerini (bellekteki yapıları), ağ üzerinden veya diskte saklanabilecek metin formatlarına (JSON, XML) dönüştürme işlemidir. Deserileştirme ise tam tersidir.
JSON (JavaScript Object Notation)
Günümüzün standart veri transfer formatıdır. C# dilinde System.Text.Json kütüphanesi (eski projelerde Newtonsoft.Json) kullanılır.
- Serileştirme: C# nesnesini alır, JSON metnine dönüştürür.
- Deserileştirme: JSON metnini alır, ilgili C# nesnesine dönüştürür.
Örnek Serileştirme:
var urun = new Urun { Adi = "Klavye", Fiyat = 150 };
string jsonString = System.Text.Json.JsonSerializer.Serialize(urun);
// jsonString = {"Adi":"Klavye","Fiyat":150}
3.13 Tasarım Kalıpları (Design Patterns)
Tasarım Kalıpları, yazılım geliştirmede sık karşılaşılan sorunlara kanıtlanmış, yeniden kullanılabilir çözümlerdir. Kodun esnekliğini, okunabilirliğini ve bakımını kolaylaştırırlar.
1. Singleton (Tekillik)
Bir sınıftan, uygulamanın çalıştığı sürece sadece tek bir nesne oluşturulmasını garanti eder. (Örn: Loglama servisi, Konfigürasyon yöneticisi).
2. Factory Method (Fabrika)
Hangi tipte nesne oluşturulacağına karar verme işini alt sınıflara veya özel bir metoda devreder. Kullanıcının new anahtar kelimesini kullanmasını engeller. (Örn: SekilFactory'nin, istenen türe göre Kare veya Daire nesnesi döndürmesi).
3. Repository (Veri Deposu)
Uygulamanın veri erişim mantığını (veritabanı, dosya sistemi, web servis) uygulamanın iş mantığından soyutlar. Böylece veritabanı teknolojisini değiştirseniz bile iş mantığı kodu değişmez.
3.14 Bağımlılık Enjeksiyonu (Dependency Injection - DI)
DI, bir sınıfın ihtiyaç duyduğu diğer nesneleri (bağımlılıklarını) kendisinin oluşturması yerine, dışarıdan sağlanması (enjekte edilmesi) ilkesidir. SOLID prensiplerinin (özellikle Dependency Inversion) ana uygulamasıdır.
Avantajları: Gevşek Bağlılık ve Test Edilebilirlik
- Gevşek Bağlılık: Sınıflar birbirine sıkı sıkıya bağlı olmaz. Bir sınıf, somut bir sınıf yerine sadece bir arayüze (interface) bağımlı olur.
- Test Edilebilirlik: Bağımlılıklar dışarıdan verildiği için, test yazarken gerçek nesneler yerine sahte (Mock) nesneler enjekte edilebilir.
Kullanım Yolu (Constructor Injection):
Bağımlılıklar, en yaygın olarak sınıfın kurucu metodu (Constructor) aracılığıyla sağlanır.
public class MusteriService // Servis, IUserDal'a bağlı
{
private readonly IUserDal _userDal;
public MusteriService(IUserDal userDal) // Bağımlılığı al!
{
_userDal = userDal;
}
}
ASP.NET Core gibi modern .NET çatılarında DI, uygulamanın temel mimarisinin bir parçasıdır.
4.1 N-tier Architecture ve SOLID prensipleri
Kurumsal uygulamaların katmanlı mimarisi ve sürdürülebilir kod için SOLID kuralları.
4.2 Clean Code ve Refactoring teknikleri
Kodun kalitesini ve okunabilirliğini artırma yöntemleri.
4.3 ASP.NET Core Web API
Modern, platformlar arası RESTful servisler geliştirme.
4.4 Entity Framework Core (ORM)
Veritabanı işlemlerini C# nesneleri üzerinden yapma.
4.5 Windows Forms / WPF / MAUI
Farklı masaüstü ve cross-platform arayüz geliştirme teknolojileri.
4.6 SQL Bağlantısı ve Veri Erişimi
Doğrudan ADO.NET (SqlConnection, SqlCommand) ile veritabanı iletişimi.
4.7 LINQ to Entities
LINQ sorgularının veritabanı SQL sorgularına otomatik çevrilmesi.
4.8 Unit Test (xUnit, NUnit, MSTest)
Kodun izole edilmiş parçalarını (Unit) otomatik olarak test etme.
4.9 Loglama (Serilog, NLog)
Uygulama hatalarını ve kritik bilgileri takip etme.
4.10 Profiling ve Bellek Optimizasyonu
Performans darboğazlarını bulma ve bellek kullanımını iyileştirme.
5.1 Nullable Reference Types
Null değer hatası riskini azaltan modern C# özelliği.
5.2 Pattern Matching (C# 8+)
Daha okunaklı ve kısa koşullu işlemler yapma.
5.3 Records, Tuples, Top-level statements
Veri transferi ve basit uygulamalar için modern dil sentaksları.
5.4 Minimal APIs (C# 10+)
Hızlı ve hafif API endpoint'leri oluşturma.
5.5 Source Generators (ileri düzey konu)
Derleme sırasında otomatik C# kodu üretme.