C# Access Modifiers: Kapıları Kilitlemek mi, Açık Bırakmak mı?
Access Modifiers: "Her Şeyi Public Yap" Diyenlere İnanmayın
Yeni başlayanların en sevdiği şey: Her şeyi public yapmak. Çalışıyor, hata vermiyor, herkes her yere erişebiliyor. Harika değil mi?
Hayır. Hiç de değil.
Bir gün iş arkadaşınız kodunuzu kullanırken, bakiye değişkenini doğrudan -1000 yapıyor. Veya yaş alanına 500 atıyor. Ya da şifre alanını log dosyasına yazıyor.
İşte Access Modifiers tam bu kaosun önüne geçmek için var.
Gerçek Hayat Analojisi: Eviniz
Evinizi düşünün:
- public: Kapınız, herkes görebilir ve girebilir (çok tehlikeli!)
- private: Yatak odanız, sadece siz girebilirsiniz
- protected: Aile odası, sadece aile üyeleri girebilir
- internal: Apartman içi ortak alan, sadece apartman sakinleri erişebilir
Şimdi bunu koda çevirelim.
public: Herkese Açık
public demek "herkes erişebilir" demek. Sınıf dışından, projeden, her yerden.
public class Kullanici
{
public string Ad { get; set; }
public string Sifre { get; set; } // ⚠️ TEHLİKE!
}
// Başka bir yerde...
Kullanici user = new Kullanici();
user.Sifre = "1234"; // Doğrudan erişim - Kötü!
Console.WriteLine(user.Sifre); // Şifre okunabiliyor - Çok kötü!Şifre public olduğu için herkes okuyabiliyor, değiştirebiliyor. Bu ciddi bir güvenlik açığı.
private: Sadece Sahibi Erişir
private üyeler sadece tanımlandığı sınıf içinden erişilebilir. Dışarıdan kimse göremez, değiştiremez.
public class BankaHesabi
{
public string HesapSahibi { get; set; }
private double _bakiye; // Sadece bu sınıf erişebilir
private string _pin; // Kesinlikle dışarı çıkmamalı
public BankaHesabi(string sahip, double baslangicBakiye, string pin)
{
HesapSahibi = sahip;
_bakiye = baslangicBakiye;
_pin = pin;
}
public void ParaYatir(double miktar)
{
if (miktar > 0)
{
_bakiye += miktar;
Console.WriteLine($"✅ {miktar:C} yatırıldı. Yeni bakiye: {_bakiye:C}");
}
}
public bool ParaCek(double miktar, string girilenPin)
{
if (girilenPin != _pin)
{
Console.WriteLine("❌ Yanlış PIN!");
return false;
}
if (miktar > _bakiye)
{
Console.WriteLine("❌ Yetersiz bakiye!");
return false;
}
_bakiye -= miktar;
Console.WriteLine($"✅ {miktar:C} çekildi. Kalan bakiye: {_bakiye:C}");
return true;
}
public double BakiyeGor()
{
return _bakiye; // Okumaya izin veriyoruz, değiştirmeye değil
}
}Kullanım:
BankaHesabi hesap = new BankaHesabi("Ahmet", 5000, "1234");
hesap.ParaYatir(1000);
// ✅ ₺1.000,00 yatırıldı. Yeni bakiye: ₺6.000,00
hesap.ParaCek(500, "1234");
// ✅ ₺500,00 çekildi. Kalan bakiye: ₺5.500,00
hesap.ParaCek(500, "4321");
// ❌ Yanlış PIN!
// Bunlar ÇALIŞMAZ:
// hesap._bakiye = 1000000; // ❌ private'a erişilemez
// Console.WriteLine(hesap._pin); // ❌ private'a erişilemezBakiye'yi doğrudan değiştiremiyorsunuz. Para yatırmak veya çekmek için metodları kullanmak zorundasınız. Bu metodlar da gerekli kontrolleri yapıyor.
protected: Aileden Olanlar Erişir
protected, sınıf içinden ve o sınıftan türeyen sınıflardan erişilebilir demek.
public class Canli
{
public string Ad { get; set; }
protected int _yasam; // Sadece Canli ve türevleri erişebilir
public Canli(string ad, int yasam)
{
Ad = ad;
_yasam = yasam;
}
public void DurumGoster()
{
Console.WriteLine($"{Ad}: Yaşam {_yasam}");
}
}
public class Oyuncu : Canli
{
public int Skor { get; set; }
public Oyuncu(string ad) : base(ad, 100)
{
Skor = 0;
}
public void HasarAl(int miktar)
{
_yasam -= miktar; // ✅ protected'a erişebilir (türemiş sınıf)
Console.WriteLine($"💥 {Ad} {miktar} hasar aldı! Kalan yaşam: {_yasam}");
if (_yasam <= 0)
{
Console.WriteLine($"☠️ {Ad} öldü!");
}
}
public void SaglikPaketiAl()
{
_yasam += 25; // ✅ protected'a erişebilir
if (_yasam > 100) _yasam = 100;
Console.WriteLine($"💚 {Ad} sağlık paketi aldı! Yaşam: {_yasam}");
}
}
// Kullanım
Oyuncu oyuncu = new Oyuncu("Ahmet");
oyuncu.DurumGoster(); // Ahmet: Yaşam 100
oyuncu.HasarAl(30); // 💥 Ahmet 30 hasar aldı! Kalan yaşam: 70
oyuncu.HasarAl(50); // 💥 Ahmet 50 hasar aldı! Kalan yaşam: 20
oyuncu.SaglikPaketiAl(); // 💚 Ahmet sağlık paketi aldı! Yaşam: 45
// Bu ÇALIŞMAZ:
// oyuncu._yasam = 9999; // ❌ protected dışarıdan erişilemez_yasam alanına Oyuncu sınıfı içinden erişebildik çünkü Canli'dan türüyor. Ama dışarıdan erişemiyoruz.
internal: Proje İçi Erişim
internal üyeler sadece aynı proje (assembly) içinden erişilebilir. Farklı projelerden erişilemez.
// MyLibrary projesi içinde
public class VeriIsleyici
{
internal string _connectionString = "Server=localhost;Database=App;";
internal void LogYaz(string mesaj)
{
Console.WriteLine($"[LOG] {mesaj}");
}
public void VeriKaydet(string veri)
{
LogYaz($"Veri kaydediliyor: {veri}"); // ✅ Aynı proje içinden erişim OK
// Kaydetme işlemleri...
}
}
// Aynı projede başka bir sınıf
public class RaporOlusturucu
{
private VeriIsleyici _isleyici = new VeriIsleyici();
public void RaporOlustur()
{
_isleyici.LogYaz("Rapor oluşturuluyor..."); // ✅ internal'a erişim OK
}
}Ama başka bir projeden:
// Farklı bir projede
VeriIsleyici isleyici = new VeriIsleyici();
isleyici.LogYaz("Test"); // ❌ internal'a dışarıdan erişilemez
isleyici.VeriKaydet("Veri"); // ✅ public metod, erişilebilirGetter ve Setter: Kontrollü Erişim
Property'lerin getter ve setter'larına farklı erişim seviyeleri verebilirsiniz:
public class Urun
{
private decimal _fiyat;
public string Ad { get; set; }
// Herkes okuyabilir, sadece sınıf içinden değiştirilebilir
public decimal Fiyat
{
get { return _fiyat; }
private set
{
if (value >= 0)
_fiyat = value;
}
}
// Sadece okunabilir property (read-only)
public string FiyatYazisi => $"₺{_fiyat:N2}";
// Sadece sınıf içinden okunabilir
private int _stok;
public int Stok
{
get => _stok;
internal set => _stok = value; // Sadece aynı proje değiştirebilir
}
public Urun(string ad, decimal fiyat, int stok)
{
Ad = ad;
Fiyat = fiyat; // private set çalışır (sınıf içindeyiz)
_stok = stok;
}
public void IndirimUygula(int yuzde)
{
Fiyat = _fiyat * (100 - yuzde) / 100; // private set çalışır
Console.WriteLine($"🏷️ %{yuzde} indirim uygulandı! Yeni fiyat: {FiyatYazisi}");
}
}Kullanım:
Urun laptop = new Urun("MacBook Pro", 85000, 10);
Console.WriteLine(laptop.Fiyat); // ✅ Okuma: 85000
Console.WriteLine(laptop.FiyatYazisi); // ✅ ₺85.000,00
// laptop.Fiyat = 100; // ❌ HATA! private set
laptop.IndirimUygula(10);
// 🏷️ %10 indirim uygulandı! Yeni fiyat: ₺76.500,00Gerçek Dünya Örneği: Kullanıcı Yönetim Sistemi
Şimdi tüm access modifier'ları birlikte kullanalım:
public class Kullanici
{
// Public: Herkes görebilir
public int Id { get; private set; }
public string KullaniciAdi { get; private set; }
public string Email { get; private set; }
public DateTime KayitTarihi { get; private set; }
// Private: Sadece bu sınıf
private string _sifreHash;
private int _basarisizGirisSayisi;
private const int MAX_BASARISIZ_GIRIS = 3;
// Protected: Bu sınıf ve türevleri
protected bool _aktifMi;
protected DateTime? _sonGirisTarihi;
// Internal: Proje içi
internal string _oturumToken;
public Kullanici(int id, string kullaniciAdi, string email, string sifre)
{
Id = id;
KullaniciAdi = kullaniciAdi;
Email = email;
_sifreHash = HashSifre(sifre);
KayitTarihi = DateTime.Now;
_aktifMi = true;
_basarisizGirisSayisi = 0;
}
public bool GirisYap(string sifre)
{
if (!_aktifMi)
{
Console.WriteLine("❌ Hesap devre dışı!");
return false;
}
if (_basarisizGirisSayisi >= MAX_BASARISIZ_GIRIS)
{
Console.WriteLine("🔒 Hesap kilitlendi! Çok fazla başarısız deneme.");
return false;
}
if (HashSifre(sifre) != _sifreHash)
{
_basarisizGirisSayisi++;
Console.WriteLine($"❌ Yanlış şifre! ({_basarisizGirisSayisi}/{MAX_BASARISIZ_GIRIS})");
return false;
}
_basarisizGirisSayisi = 0;
_sonGirisTarihi = DateTime.Now;
_oturumToken = Guid.NewGuid().ToString();
Console.WriteLine($"✅ Hoş geldin, {KullaniciAdi}!");
return true;
}
public bool SifreDegistir(string eskiSifre, string yeniSifre)
{
if (HashSifre(eskiSifre) != _sifreHash)
{
Console.WriteLine("❌ Mevcut şifre yanlış!");
return false;
}
if (yeniSifre.Length < 6)
{
Console.WriteLine("❌ Şifre en az 6 karakter olmalı!");
return false;
}
_sifreHash = HashSifre(yeniSifre);
Console.WriteLine("✅ Şifre başarıyla değiştirildi!");
return true;
}
private string HashSifre(string sifre)
{
// Gerçek uygulamada güvenli hash algoritması kullanılmalı
return Convert.ToBase64String(
System.Text.Encoding.UTF8.GetBytes(sifre + "salt123")
);
}
public void ProfilGoster()
{
Console.WriteLine($"\n👤 KULLANICI PROFİLİ");
Console.WriteLine($"ID: {Id}");
Console.WriteLine($"Kullanıcı Adı: {KullaniciAdi}");
Console.WriteLine($"Email: {Email}");
Console.WriteLine($"Kayıt Tarihi: {KayitTarihi:dd/MM/yyyy}");
Console.WriteLine($"Durum: {(_aktifMi ? "Aktif ✅" : "Pasif ❌")}");
Console.WriteLine($"Son Giriş: {_sonGirisTarihi?.ToString("dd/MM/yyyy HH:mm") ?? "Henüz giriş yapılmadı"}");
}
}
// Admin sınıfı - Kullanici'dan türüyor
public class Admin : Kullanici
{
public string Yetki { get; set; }
public Admin(int id, string kullaniciAdi, string email, string sifre, string yetki)
: base(id, kullaniciAdi, email, sifre)
{
Yetki = yetki;
}
public void KullaniciBanla(Kullanici kullanici)
{
// protected alana erişim - türemiş sınıf olduğu için OK
// Ama kullanici._aktifMi = false; ÇALIŞMAZ - başka bir instance
Console.WriteLine($"🚫 {kullanici.KullaniciAdi} banlandı!");
}
public void SistemLogGor()
{
// internal alana erişim - aynı projede olduğu için OK
Console.WriteLine($"📋 Admin {KullaniciAdi} logları görüntülüyor...");
Console.WriteLine($"🔑 Oturum Token: {_oturumToken}");
}
}Kullanım:
// Normal kullanıcı
Kullanici ali = new Kullanici(1, "ali_dev", "[email protected]", "sifre123");
ali.GirisYap("yanlis"); // ❌ Yanlış şifre! (1/3)
ali.GirisYap("yanlis"); // ❌ Yanlış şifre! (2/3)
ali.GirisYap("sifre123"); // ✅ Hoş geldin, ali_dev!
ali.ProfilGoster();
ali.SifreDegistir("sifre123", "yeniSifre456");
// ✅ Şifre başarıyla değiştirildi!
// Bunlar ÇALIŞMAZ:
// ali._sifreHash = "hack"; // ❌ private
// ali._aktifMi = false; // ❌ protected
// Admin işlemleri
Admin admin = new Admin(0, "superadmin", "[email protected]", "admin123", "FullAccess");
admin.GirisYap("admin123");
admin.SistemLogGor();Özet Tablosu
| Modifier | Aynı Sınıf | Türemiş Sınıf | Aynı Proje | Dışarı |
|---|---|---|---|---|
| public | ✅ | ✅ | ✅ | ✅ |
| private | ✅ | ❌ | ❌ | ❌ |
| protected | ✅ | ✅ | ❌ | ❌ |
| internal | ✅ | ❌* | ✅ | ❌ |
| protected internal | ✅ | ✅ | ✅ | ❌ |
| private protected | ✅ | ✅** | ❌ | ❌ |
*Internal'a türemiş sınıf aynı projede ise erişebilir.
**Sadece aynı projedeki türemiş sınıflar erişebilir.
Ne Zaman Hangisini Kullanmalı?
| Durum | Önerilen |
|---|---|
| Dışarıdan erişilmeli | public |
| Sadece sınıf içinde | private (varsayılan olarak bunu düşünün) |
| Türemiş sınıflar da erişsin | protected |
| Proje içinde paylaşılsın | internal |
| Hassas veri (şifre, token) | private + metodlarla kontrollü erişim |
Encapsulation'a Giriş
Access modifier'lar aslında OOP'nin temel prensiplerinden biri olan Encapsulation (Kapsülleme) kavramının yapı taşlarıdır.
Encapsulation der ki:
- Veriyi gizle (
private) - Kontrollü erişim sağla (metodlar ve property'ler)
- Nesnenin iç durumunu koru
Bir sonraki yazıda Encapsulation'ı daha derinlemesine inceleyeceğiz.
Sonuç: Varsayılan Olarak Private Düşünün
Altın kural: Her şey private ile başlasın, ihtiyaç oldukça açın.
// ❌ Kötü - Her şey public
public class Kotu
{
public string sifre;
public int bakiye;
}
// ✅ İyi - Minimum gerekli erişim
public class Iyi
{
private string _sifre;
private int _bakiye;
public int Bakiye => _bakiye; // Sadece okuma
public void SifreDogrula(string sifre) { ... }
public void BakiyeDegistir(int miktar) { ... }
}Temiz ve güvenli kodlar! 🚀
Bu yazı, C# ile OOP serisinin dördüncü yazısıdır. Bir önceki yazıda Constructor konusunu, bir sonraki yazıda Encapsulation prensibini ele alacağız.