C# Access Modifiers: Kapıları Kilitlemek mi, Açık Bırakmak mı?
Son güncelleme:
3 dk okuma

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; C# dil referansındaki erişim belirleyiciler sayfası altı erişim seviyesinin tamamını tanımlıyor.
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. Buradaki : Canli sözdizimi kalıtım; virtual ve override ile birlikte nasıl işlediğini kalıtım ve polymorphism rehberinde anlatıyorum.
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
Class ve Object yazısında property'nin field'dan neden daha iyi olduğunu görmüştük; şimdi bir adım ileri gidiyoruz. Property'lerin getter ve setter'larına, C# property dokümantasyonunda anlatıldığı gibi, 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'ye giriş yazısında tanıştığımız dört prensipten 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
Serinin devamında sıra kalıtım ve polymorphism konusunda; protected'ın gerçek gücü orada ortaya çıkıyor. Soyutlama tarafı için de interface ve abstract class farkı yazısı var.
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 ele aldık; devamında kalıtım ve polymorphism var.
Sık Sorulan Sorular
Access modifier yazmazsam varsayılan ne olur?
Sınıf üyeleri (field, property, metod) için varsayılan private'tır. Namespace düzeyinde tanımlanan bir class için ise varsayılan internal'dır; dışarıdan kullanılacaksa açıkça public yazmanız gerekir.
protected internal ile private protected arasındaki fark nedir?
protected internal "türemiş sınıf VEYA aynı assembly" demektir; ikisinden biri yeterlidir. private protected ise "türemiş sınıf VE aynı assembly" demektir; yalnızca aynı projedeki türemiş sınıflar erişebilir.
private set ile init arasında ne fark var?
private set sınıfın içinden her zaman değer atamaya izin verir. init ise değeri yalnızca nesne oluşturulurken (constructor ya da object initializer ile) atamanıza izin verir, sonrasında değiştirilemez; değişmez (immutable) nesneler için tercih edilir.
İlgili Yazılar
OOP'ye Giriş: Neden Prosedürel Programlama Artık Yetmiyor?
Prosedürel programlamadan nesne yönelimliye geçiş. Spaghetti koddan kurtulup gerçek dünya problemlerini koda dönüştürmeyi keşfedin.
C# Constructor: Nesne Fabrikasının Gizli Kahramanı
Constructor nedir, neden gerekli? Default constructor, parametreli constructor ve constructor overloading kavramlarını gerçek dünya örnekleriyle keşfediyoruz.
C# Class ve Object: Taslak mı, Ürün mü?
OOP'nin temel yapı taşları Class ve Object'i kek kalıbı analojisiyle öğrenin. Property, Method ve Instance kavramları örneklerle.