Ahmet Balaman LogoAhmet Balaman

C# Access Modifiers: Kapıları Kilitlemek mi, Açık Bırakmak mı?

personAhmet Balaman
calendar_today
C#OOPAccess ModifiersEncapsulation.NETProgramlama Temelleri

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şilemez

Bakiye'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şilebilir

Getter 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,00

Gerç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.

Yorumlar