Physical Address

304 North Cardinal St.
Dorchester Center, MA 02124

C# Intermediate: Classes, Interfaces and OOP

Bu makalede, C# Intermediate seviyesindeki konulara odaklanarak, sınıflar, arayüzler ve nesne yönelimli programlamayı (OOP) derinlemesine inceleyeceğiz. Bu konseptler, C# dilinde yazılım geliştirme sürecinde temel taşlardır ve kodunuzun daha organize, esnek ve sürdürülebilir olmasına olanak tanır.

Bilgisayar programlaması, günümüzde birçok endüstri ve sektörde hayati bir rol oynamaktadır. Programlamayı öğrenenler, sadece problem çözme yeteneklerini geliştirmekle kalmaz, aynı zamanda modern yazılım geliştirme dünyasının temel prensiplerini de kavrarlar. Bu noktada, C# gibi güçlü bir programlama dilinin ortaya çıkışı, yazılım geliştiricilerin araç kutularına önemli bir yetenek katmıştır.

Bu makalede, C# Intermediate seviyesindeki konulara odaklanarak, sınıflar, arayüzler ve nesne yönelimli programlamayı (OOP) derinlemesine inceleyeceğiz. Bu konseptler, C# dilinde yazılım geliştirme sürecinde temel taşlardır ve kodunuzun daha organize, esnek ve sürdürülebilir olmasına olanak tanır.

Sınıflar ve Nesne Yönelimli Programlama:

C# dilinde sınıflar, nesne yönelimli programlamanın temelini oluşturur. Sınıflar, bir nesnenin özelliklerini (veri alanları) ve davranışlarını (metodlar) içeren yapı taşlarıdır. Bu, gerçek dünyadaki nesnelerin modellenmesine ve kodun daha anlaşılır ve düzenli olmasına olanak tanır. Makalemizde, sınıfların nasıl oluşturulduğunu, özelliklerin nasıl tanımlandığını ve metodların nasıl kullanıldığını detaylı bir şekilde ele alacağız.

Arayüzler ve Soyut Sınıflar:

Arayüzler, C# dilinde çoklu kalıtımın temelini oluşturan önemli bir konsepttir. Arayüzler, bir sınıfın veya nesnenin belirli bir davranış kümesini uygulamasını sağlar. Ayrıca, soyut sınıfların kullanımıyla ilgili olarak, kodunuzu daha modüler ve genişletilebilir hale getirmenin yollarını keşfedeceğiz.

Polimorfizm ve Kalıtım:

Polimorfizm ve kalıtım, nesne yönelimli programlamanın temel prensiplerindendir. Bu makalede, bu kavramların ne olduğunu, nasıl uygulandığını ve kodunuzun daha esnek ve genişletilebilir olmasını sağlamak için nasıl kullanılacağını anlatacağız.

Bu makale dizisinde, C# Intermediate seviyesindeki konulara derinlemesine bir bakış yaparak, bu konseptleri anlamanıza ve kendi yazılım projelerinizde daha etkili bir şekilde kullanmanıza yardımcı olmayı amaçlıyoruz. İlerleyen yazılarda, her konuyu daha ayrıntılı bir şekilde inceleyerek, gerçek dünya uygulamalarında nasıl kullanabileceğinizi göstereceğiz. Başlamaya hazır mısınız?

Classes (Sınıflar) – Introduction to Classes (Sınıflara Giriş)

C# dilinde, programlamada temel yapı taşlarından biri olan sınıflar, nesne yönelimli programlamanın (OOP) merkezinde yer alır. Sınıflar, kodunuzu organize etmek, veriyi düzenlemek ve işlevselliği gruplamak için kullanılan önemli bir araçtır. Bu bölümde, C# sınıflarına giriş yaparak, bu güçlü konseptin temellerini anlamaya odaklanacağız.

Sınıflar Nedir?

Bir sınıf, bir nesnenin özelliklerini (veri alanları) ve davranışlarını (metodlar) içeren bir taslağı temsil eder. Gerçek dünyadan bir örnekle açıklamak gerekirse, bir “Araba” sınıfı, araçların renkleri, markaları, hızları gibi özelliklerini içerirken, “Sürüş” metodu gibi davranışlarını da içerebilir.

public class Araba
{
    // Özellikler
    public string Renk { get; set; }
    public string Marka { get; set; }
    public int Hiz { get; set; }

    // Davranış
    public void Sur()
    {
        Console.WriteLine("Araba sürülüyor...");
    }
}

Yukarıdaki örnekte, “Araba” adında bir sınıf tanımladık. Bu sınıf, bir aracın sahip olabileceği özellikleri ve sürüş davranışını içerir.

Sınıfların Avantajları

  1. Modülerlik: Sınıflar, kodunuzu modüler hale getirmenin bir yoludur. Her sınıf, belirli bir sorumluluğa sahip olabilir, bu da kodunuzu daha okunabilir ve yönetilebilir kılar.
  2. Nesne Yönelimli Programlama (OOP): Sınıflar, nesne yönelimli programlamanın temelini oluşturur. Bu, gerçek dünyadaki varlıkları modellemenize ve problemleri daha iyi çözmenize olanak tanır.
  3. Kodun Yeniden Kullanılabilirliği: Bir sınıfı oluşturduktan sonra, aynı sınıfı başka projelerde veya farklı kısımlarda rahatlıkla kullanabilirsiniz.

Sınıfın Kullanımı

Sınıfı kullanmak için, bir nesne oluşturmanız ve ardından bu nesne üzerinden sınıfın özelliklerine ve metodlarına erişmeniz gerekir.

Araba myAraba = new Araba();
myAraba.Renk = "Kırmızı";
myAraba.Marka = "Toyota";
myAraba.Hiz = 120;

myAraba.Sur(); // Araba sürülüyor...

Yukarıdaki örnek, “Araba” sınıfından bir nesne oluşturur ve bu nesnenin özelliklerini ayarlar, ardından sınıfın metodunu çağırır.

Classes (Sınıflar) – Constructors (Yapıcı Metotlar)

C# dilinde, sınıfların temel yapı taşları olan “constructors” veya Türkçe adıyla “yapıcı metotlar”, bir sınıfın bir örneği (nesnesi) oluşturulduğunda çalışan özel metotlardır. Bu bölümde, sınıf yapıcı metotlarına derinlemesine bir bakış yaparak, bu önemli konseptin nasıl kullanılacağını anlayacağız.

Yapıcı Metot Nedir?

Bir yapıcı metot, sınıfın bir örneği oluşturulduğunda otomatik olarak çalışan ve sınıfın başlatılmasını sağlayan özel bir metottur. Bu metotlar genellikle sınıfın özelliklerini başlatmak, başlangıç değerleri atamak veya başka ön ayarlamaları gerçekleştirmek için kullanılır.

public class Araba
{
    // Özellikler
    public string Renk { get; set; }
    public string Marka { get; set; }
    public int Hiz { get; set; }

    // Yapıcı Metot
    public Araba()
    {
        Renk = "Beyaz";
        Marka = "Unknown";
        Hiz = 0;
    }

    // Diğer Metotlar
    public void Sur()
    {
        Console.WriteLine("Araba sürülüyor...");
    }
}

Yukarıdaki örnekte, “Araba” sınıfında bir yapıcı metot (constructor) tanımladık. Bu metot, bir araç nesnesi oluşturulduğunda, aracın başlangıç özelliklerini belirler.

Farklı Yapıcı Metot Overloading

C# dilinde, aynı isimde birden fazla yapıcı metot tanımlayabilirsiniz. Bu duruma yapıcı metot overloading denir. Aşağıdaki örnekte, parametreli bir yapıcı metot ekleyerek, kullanıcıya başlangıç değerlerini belirleme esnekliği sağlıyoruz.

public class Araba
{
    // Özellikler
    public string Renk { get; set; }
    public string Marka { get; set; }
    public int Hiz { get; set; }

    // Yapıcı Metot
    public Araba()
    {
        Renk = "Beyaz";
        Marka = "Unknown";
        Hiz = 0;
    }

    // Parametreli Yapıcı Metot Overloading
    public Araba(string renk, string marka, int hiz)
    {
        Renk = renk;
        Marka = marka;
        Hiz = hiz;
    }

    // Diğer Metotlar
    public void Sur()
    {
        Console.WriteLine("Araba sürülüyor...");
    }
}

Bu sayede, kullanıcı isteğine göre ya parametresiz yapıcı metotu kullanarak ya da istediği başlangıç değerlerini belirterek bir nesne oluşturabilir.

Yapıcı metotlar, sınıfların başlatılması ve özelliklerin ayarlanması süreçlerinde büyük öneme sahiptir. Bu bölümde, sınıf yapıcı metotlarının temelini anladık. İlerleyen bölümlerde, bu konuyu daha da derinleştirerek, daha karmaşık senaryolarda nasıl kullanabileceğimizi öğreneceğiz.

Classes (Sınıflar) – Object Initializers (Nesne Başlatıcıları)

C# dilinde, sınıfların özelliklerini hızlı bir şekilde başlatmak için kullanılan “Object Initializers” veya Türkçe adıyla “Nesne Başlatıcıları”, kod yazımını daha kısa ve okunabilir hale getiren önemli bir özelliktir. Bu bölümde, nesne başlatıcılarını detaylı bir şekilde inceleyerek, bu pratik konseptin nasıl kullanılacağını öğreneceğiz.

Nesne Başlatıcıları Nedir?

Nesne başlatıcıları, bir nesne oluştururken ve özelliklerini ayarlamak istediğinizde kullanılan kısa bir sözdizimidir. Bu, genellikle sınıfları daha açık ve kompakt bir şekilde tanımlamanıza olanak tanır.

public class Araba
{
    // Özellikler
    public string Renk { get; set; }
    public string Marka { get; set; }
    public int Hiz { get; set; }

    // Yapıcı Metot
    public Araba()
    {
        Renk = "Beyaz";
        Marka = "Unknown";
        Hiz = 0;
    }

    // Diğer Metotlar
    public void Sur()
    {
        Console.WriteLine("Araba sürülüyor...");
    }
}

Yukarıdaki örnekteki “Araba” sınıfını, nesne başlatıcıları kullanarak şu şekilde daha kısa bir şekilde tanımlayabiliriz:

Araba myAraba = new Araba
{
    Renk = "Kırmızı",
    Marka = "Toyota",
    Hiz = 120
};

Bu örnekte, nesne başlatıcısı kullanarak “Araba” sınıfından bir nesne oluşturuyoruz ve özelliklerini aynı satırda belirliyoruz.

Nesne Başlatıcılarının Avantajları

  1. Okunabilirlik: Kodunuzu daha temiz ve okunabilir hale getirir. Nesne başlatıcıları, özellikleri belirleme sürecini daha açık ve kısa bir şekilde sunar.
  2. Kod Yazım Hızı: Sınıfların özelliklerini hızlı bir şekilde başlatmanıza olanak tanır, bu da kod yazma sürecinizi hızlandırır.
  3. Değer Atama Esnekliği: Nesne başlatıcıları, sadece belirli özelliklere değer atamak istediğiniz durumları yönetir ve diğer özellikleri varsayılan değerleriyle bırakır.

Karmaşık Nesne Başlatıcıları

Nesne başlatıcıları, karmaşık nesneleri oluştururken de kullanılabilir. İç içe geçmiş nesneleri ve koleksiyonları başlatmak için de kullanışlıdır.

public class ArabaParki
{
    public List<Araba> Arabalar { get; set; }

    public ArabaParki()
    {
        Arabalar = new List<Araba>();
    }
}

// Karmaşık Nesne Başlatıcısı
ArabaParki arabaParki = new ArabaParki
{
    Arabalar = new List<Araba>
    {
        new Araba { Renk = "Mavi", Marka = "Ford", Hiz = 100 },
        new Araba { Renk = "Siyah", Marka = "Chevrolet", Hiz = 110 }
    }
};

Bu bölümde, C# nesne başlatıcılarına odaklandık ve sınıfları hızlı bir şekilde başlatmanın ve okunabilirliği artırmanın yollarını inceledik.

Classes (Sınıflar) – Methods (Metotlar)

C# dilinde, sınıfların temel yapı taşları olan metotlar, bir sınıf içinde belirli bir davranışı veya işlemi gerçekleştiren kod bloklarıdır. Bu bölümde, C# metotlarına detaylı bir bakış yaparak, bu temel kavramın nasıl kullanılacağını ve kodunuzu nasıl daha modüler hale getireceğinizi anlayacağız.

Metot Nedir?

Metotlar, belirli bir işlemi gerçekleştiren, başka bir deyişle belirli bir davranışı içeren kod bloklarıdır. Sınıflar içinde tanımlanan metotlar, genellikle sınıfın temel işlevselliğini ve davranışlarını tanımlar.

public class HesapMakinesi
{
    // Toplama Metodu
    public int Topla(int sayi1, int sayi2)
    {
        return sayi1 + sayi2;
    }

    // Çıkarma Metodu
    public int Cikar(int sayi1, int sayi2)
    {
        return sayi1 - sayi2;
    }
}

Yukarıdaki örnekte, “HesapMakinesi” adlı bir sınıfı tanımladık ve bu sınıfta toplama ve çıkarma işlemlerini gerçekleştiren iki farklı metot bulunmaktadır.

Metotların Avantajları

  1. Modülerlik: Metotlar, kodunuzu modüler hale getirmenizi sağlar. Belirli bir işlevselliği tek bir yerde tanımlayarak, kodunuzu daha düzenli ve anlaşılır yapabilirsiniz.
  2. Tekrar Kullanılabilirlik: Metotları başka projelerde veya aynı projenin farklı kısımlarında kolayca kullanabilirsiniz, bu da kodunuzu tekrar kullanılabilir kılar.
  3. Okunabilirlik: Belirli bir işlemi gerçekleştiren kod bloğunu bir metot içinde tanımlamak, kodun okunabilirliğini artırır ve karmaşıklığı azaltır.

Metot Parametreleri ve Dönüş Değeri

Metotlar parametreleri alabilir ve bir değer döndürebilir. Parametreler, metotun işlevselliğini belirleyen girdi değerleridir. Dönüş değeri ise metotun bir sonuç üretip üretmediğini belirtir.

public class Matematik
{
    // Parametreli ve Dönüş Değeri Olan Metot
    public int KareAl(int sayi)
    {
        return sayi * sayi;
    }

    // Parametreli Ancak Dönüş Değeri Olmayan Metot
    public void Yazdir(int sayi)
    {
        Console.WriteLine(sayi);
    }

    // Parametresiz ve Dönüş Değeri Olan Metot
    public string Merhaba()
    {
        return "Merhaba!";
    }

    // Parametresiz ve Dönüş Değeri Olmayan Metot
    public void EkranaYaz()
    {
        Console.WriteLine("Ekrana yazdım!");
    }
}

Bu örnekte, “Matematik” adlı bir sınıfı tanımladık ve farklı türde metotları inceledik. Her bir metotun belirli bir işlevselliği vardır ve kullanım amaçlarına göre farklıdır.

Metotlar, C# dilinde yazılım geliştirmenin temel yapı taşlarından biridir ve kodunuzun düzenli, modüler ve yeniden kullanılabilir olmasını sağlar.

Classes (Sınıflar) – Fields (Alanlar)

C# dilinde, sınıfların temel yapı taşlarından biri olan “fields” veya Türkçe adıyla “alanlar”, sınıfların içindeki veri saklama ve erişme amacı taşıyan değişkenlerdir. Bu bölümde, C# sınıf alanlarına detaylı bir bakış yaparak, bu kavramın nasıl kullanılacağını ve sınıf içindeki verilerin nasıl yönetilebileceğini anlayacağız.

Alan Nedir?

Bir alan, bir sınıf içinde belirli bir veriyi saklayan değişken türüdür. Genellikle sınıfın özelliklerini temsil eder ve bu özelliklere erişmek ve bu özelliklere değer atamak için kullanılır.

public class Ogrenci
{
    // Alanlar
    public string Adi;
    public string Soyadi;
    public int Yas;
}

Yukarıdaki örnekte, “Ogrenci” adlı bir sınıf tanımladık ve bu sınıf içinde “Adi”, “Soyadi” ve “Yas” adında üç farklı alan bulunmaktadır.

Alanların Erişim Modifikatörleri

C# dilinde, alanlara erişimi kontrol etmek için erişim modifikatörleri kullanılabilir. Erişim modifikatörleri, alanın sadece sınıf içinde mi yoksa başka sınıflardan da mı erişilebileceğini belirler.

public class Ogrenci
{
    // Public Alan
    public string Adi;

    // Private Alan
    private string Soyadi;

    // Internal Alan
    internal int Yas;
}

Yukarıdaki örnekte, “Adi” alanı herhangi bir yerden erişilebilirken, “Soyadi” alanı sadece bu sınıf içinde erişilebilir ve “Yas” alanı sadece aynı assembly içindeki sınıflar tarafından erişilebilir.

Alanlara Başlangıç Değeri Atama

Alanlara başlangıç değeri atamak, sınıfın başlangıç durumunu belirlemek açısından önemlidir. Bu işlem, sınıfın örnekleri oluşturulduğunda, alanların varsayılan değerlerini belirler.

public class Araba
{
    // Başlangıç Değeri Atanmış Alan
    public string Renk = "Beyaz";

    // Başlangıç Değeri Atanmamış Alan
    public string Marka;
}

Yukarıdaki örnekte, “Renk” alanına başlangıç değeri atandı, bu nedenle bir Araba nesnesi oluşturulduğunda “Renk” alanı otomatik olarak “Beyaz” değeriyle başlar. “Marka” alanına ise başlangıç değeri atanmadığı için bu alanın başlangıç değeri null olacaktır.

Classes (Sınıflar) – Access Modifiers (Erişim Belirleyiciler)

C# dilinde, sınıfların içindeki elemanlara (alanlar, metotlar, özellikler vb.) erişim düzeyini kontrol etmek için kullanılan erişim belirleyiciler, kodunuzu daha güvenli, düzenli ve bakımı kolay hale getiren önemli bir özelliktir. Bu bölümde, C# dilindeki erişim belirleyicileri inceleyerek, sınıflar içindeki elemanlara nasıl erişilebileceğini ve bu erişim belirleyicilerinin nasıl kullanılacağını anlayacağız.

Erişim Belirleyicilerin Türleri

Public (Genel): Public erişim belirleyici, elemana her yerden erişime izin verir. Yani, elemanlar, aynı projedeki diğer sınıflardan veya diğer projelerden erişilebilir.

public class Araba
{
    // Public Alan
    public string Marka;
    
    // Public Metot
    public void GazVer()
    {
        Console.WriteLine("Gaz veriliyor...");
    }
}

Private (Özel): Private erişim belirleyici, elemana sadece tanımlandığı sınıf içinden erişime izin verir. Diğer sınıflardan veya projelerden bu elemanlara doğrudan erişim mümkün değildir.

public class Araba
{
    // Private Alan
    private string Model;
    
    // Private Metot
    private void SogutmaSistemiCalistir()
    {
        Console.WriteLine("Soğutma sistemi çalıştırılıyor...");
    }
}

Protected (Korumalı): Protected erişim belirleyici, elemana tanımlandığı sınıfın içinden veya bu sınıftan türetilmiş alt sınıflardan erişime izin verir.

public class Personel
{
    // Protected Alan
    protected string Isim;
    
    // Protected Metot
    protected void MesaiyeBasla()
    {
        Console.WriteLine("Mesaiye başlanıyor...");
    }
}

Internal (İç): Internal erişim belirleyici, elemana sadece tanımlandığı assembly içinden erişime izin verir. Yani, aynı projedeki diğer sınıflar bu elemanlara erişebilir, ancak başka bir assembly’den erişim mümkün değildir.

public class Proje
{
    // Internal Alan
    internal string ProjeAdi;
    
    // Internal Metot
    internal void ProjeDetaylari()
    {
        Console.WriteLine("Proje detayları gösteriliyor...");
    }
}

Protected Internal (Korumalı İç): Protected Internal erişim belirleyici, hem Protected hem de Internal erişim belirleyicilerinin kombinasyonudur. Yani, hem tanımlandığı sınıfın içinden hem de türetilmiş alt sınıflardan veya aynı assembly içindeki diğer sınıflardan erişime izin verir.

public class BankaHesabi
{
    // Protected Internal Alan
    protected internal decimal Bakiye;
    
    // Protected Internal Metot
    protected internal void ParaCek(decimal miktar)
    {
        Console.WriteLine($"{miktar} tutarında para çekiliyor...");
    }
}

Erişim Belirleyicilerin Kullanımı

Erişim belirleyiciler, sınıf elemanlarına doğrudan uygulanır. Örneğin, bir alanı veya bir metodu tanımlarken hangi erişim belirleyicisini kullanacağınızı belirtmelisiniz.

public class Ogrenci
{
    // Public alan örneği
    public string Adi;

    // Private metot örneği
    private void SinavSonuclari()
    {
        Console.WriteLine("Sınav sonuçları gösteriliyor...");
    }
}

Sınıfların erişim belirleyicileri de bulunabilir. Varsayılan olarak, bir sınıfın erişim belirleyicisi “internal” olarak ayarlanır, yani sadece aynı assembly içindeki diğer sınıflar tarafından erişilebilir. Ancak, ihtiyaca bağlı olarak “public” veya “private” olarak da değiştirilebilir.

// Public erişim belirleyicisine sahip sınıf
public class HalkaAcikSinif
{
    // Sınıf içeriği buraya yazılır...
}

Erişim belirleyiciler, sınıfların, elemanların ve metodların güvenliğini, düzenini ve bakımını yönetmek için güçlü bir araçtır. Bu bölümde, C# dilindeki erişim belirleyicilerini anladık ve bu belirleyicilerin nasıl kullanılacağını öğrendik.

Classes (Sınıflar) – Properties (Özellikler)

C# dilinde, sınıfların içindeki verilere daha güvenli ve düzenli bir erişim sağlamak için kullanılan “properties” veya Türkçe adıyla “özellikler”, sınıfların temel yapı taşlarından biridir. Bu bölümde, C# özelliklerine derinlemesine bir bakış yaparak, bu kavramın nasıl kullanılacağını ve neden önemli olduğunu anlayacağız.

Özellik Nedir?

Özellikler, sınıf içindeki bir alanın (field) değerine erişim sağlamak veya bu değeri değiştirmek için kullanılan bir tür metod veya alanlardır. Özellikler, genellikle sınıfın içindeki verilere daha güvenli bir şekilde erişmek ve manipüle etmek için kullanılır.

public class Ogrenci
{
    // Alan (Field)
    private string adi;

    // Özellik (Property)
    public string Adi
    {
        get { return adi; }
        set { adi = value; }
    }
}

Yukarıdaki örnekte, “Ogrenci” adlı bir sınıfı tanımladık ve bu sınıf içinde “adi” adlı bir alanı (field) ve buna erişimi sağlayan bir “Adi” adlı bir özelliği (property) tanımladık.

Get ve Set Metotları

Özellikler, genellikle bir “get” (al) ve bir “set” (at) metodu içerir. “Get” metodu, özelliğin değerini okumak için kullanılırken, “set” metodu özelliğe yeni bir değer atamak için kullanılır.

public class Araba
{
    // Private alan (field)
    private string model;

    // Public özellik (property)
    public string Model
    {
        get { return model; }
        set { model = value; }
    }
}

Yukarıdaki örnekte, “Araba” sınıfında “model” adında bir özel alan (field) ve buna erişimi sağlamak için “Model” adında bir özellik (property) bulunmaktadır.

Kısa Form Özellikler (Auto-Implemented Properties)

C# dilinde, özellikle sadece bir alanın değerini okuma ve atama işlevi dışında herhangi bir ek işlem yapmadığınız durumlarda, daha kısa bir yazım stili olan otomatik özellikleri (auto-implemented properties) kullanabilirsiniz.

public class Bilgisayar
{
    // Otomatik özellik (Auto-Implemented Property)
    public string Marka { get; set; }
}

Yukarıdaki örnekte, “Bilgisayar” sınıfında “Marka” adında bir özellik (property) otomatik olarak tanımlanmıştır. Bu durumda, C# derleyicisi, arkada otomatik olarak bir alan (field) oluşturur ve “get” ve “set” metotlarını ekler.

ReadOnly ve WriteOnly Özellikler

Özelliklerin sadece okunabilir (readonly) veya sadece yazılabilir (writeonly) olmasını sağlamak için özel bir kullanım da mümkündür.

public class Kitap
{
    // Sadece okunabilir (readonly) özellik
    public string Isim { get; }

    // Sadece yazılabilir (writeonly) özellik
    public int SayfaSayisi { private get; set; }
}

Yukarıdaki örnekte, “Kitap” sınıfında “Isim” özelliği sadece okunabilir (readonly), “SayfaSayisi” özelliği ise sadece yazılabilir (writeonly) olarak tanımlanmıştır.

Özelliklerin Avantajları

  1. Güvenli Erişim Kontrolü: Özellikler, sınıf içindeki verilere güvenli bir erişim sağlar. Bu, sınıfın iç yapısını korur ve dış dünyadan gelebilecek hatalı veya istenmeyen müdahaleleri önler.
  2. Kod Okunabilirliği: Kodunuzun daha okunabilir ve anlaşılır olmasını sağlar. Özellikler, sınıfın iç yapılarına yapılan erişimi açık bir şekilde tanımlar.
  3. Kod Bakımı ve Esneklik: Özellikler, sınıf içindeki verilere erişimi kontrol etmekle birlikte, ilerleyen zamanlarda sınıf içindeki mantığı değiştirmenize veya yeni işlevsellik eklemenize olanak tanır.

Özelliklerin Kullanımı

Özelliklere erişim, sınıfın dışından aşağıdaki gibi gerçekleştirilir:

Ogrenci ogrenci = new Ogrenci();
ogrenci.Adi = "Ahmet"; // Özelliğe değer atama
string adi = ogrenci.Adi; // Özelliği okuma

Yukarıdaki örnekte, “Ogrenci” sınıfından bir nesne oluşturuldu ve bu nesne üzerinden “Adi” özelliğine değer atandı ve bu özellik okundu.

Classes (Sınıflar) – Indexers (İndeksleyiciler)

C# dilinde, sınıfların içinde diziler gibi davranabilen ve indeksleme (indexing) işlevselliği sağlayan “indexers” veya Türkçe adıyla “İndeksleyiciler”, programlamada güçlü ve esnek bir özelliktir. Bu bölümde, C# sınıflarındaki indeksleyicilere derinlemesine bir bakış yaparak, bu kavramın ne olduğunu ve nasıl kullanılacağını anlayacağız.

İndeksleyiciler Nedir?

İndeksleyiciler, sınıfların nesnelerini diziler gibi davranmalarını sağlayan özel bir tür özelliktir. Bu, nesnelerin belirli bir indeksleme işlemine tabi tutularak, ilgili verilere erişim veya bu verilere değer atama olanağı tanır.

public class OgrenciGrubu
{
    // Özel bir dizi alanı (field)
    private string[] ogrenciIsimleri = new string[10];

    // İndeksleyici (indexer) tanımı
    public string this[int indeks]
    {
        get { return ogrenciIsimleri[indeks]; }
        set { ogrenciIsimleri[indeks] = value; }
    }
}

Yukarıdaki örnekte, “OgrenciGrubu” adlı bir sınıf tanımladık ve bu sınıf içinde “ogrenciIsimleri” adında bir dizi alanı ve buna erişimi sağlamak için bir indeksleyici (indexer) tanımladık.

İndeksleyici Kullanımı

İndeksleyicileri kullanarak sınıf nesnelerine diziler gibi erişebilir ve bu erişim üzerinden işlemler gerçekleştirebilirsiniz.

OgrenciGrubu grup = new OgrenciGrubu();
grup[0] = "Ahmet";
grup[1] = "Ayşe";

string isim = grup[0];
Console.WriteLine(isim); // Çıktı: Ahmet

Yukarıdaki örnekte, “OgrenciGrubu” sınıfından bir nesne oluşturuldu ve bu nesne üzerinden indeksleyici kullanılarak öğrenci isimlerine değer atandı ve okundu.

Birden Fazla İndeks Parametresi

İndeksleyicilere birden fazla indeks parametresi eklemek mümkündür. Bu, sınıfa birden fazla boyutta indeksleme yeteneği kazandırır.

public class Matris
{
    private int[,] veriler = new int[3, 3];

    // İki boyutlu indeksleyici (indexer) tanımı
    public int this[int satir, int sutun]
    {
        get { return veriler[satir, sutun]; }
        set { veriler[satir, sutun] = value; }
    }
}

Yukarıdaki örnekte, “Matris” adlı bir sınıf tanımladık ve iki boyutlu bir diziyi indeksleyerek bu sınıfın özelliklerine erişim sağlamak için bir indeksleyici tanımladık.

İndeksleyicilerin Avantajları

  1. Esnek Kullanım: İndeksleyiciler, sınıf nesnelerine indeksleme yeteneği ekleyerek daha esnek ve genel kullanım sağlar.
  2. Dizi Benzeri Davranış: İndeksleyiciler, kullanım açısından dizilere benzer bir davranış sergileyerek kodun daha anlaşılır olmasını sağlar.
  3. Kapsülleme: İndeksleyiciler, sınıf içindeki verilere erişimi kontrol etmek ve gerekirse işlemleri yönlendirmek için kapsülleme sağlar.

İndeksleyicilerin Kullanımı

İndeksleyicilere erişim, sınıf nesnesi üzerinden indeks kullanarak gerçekleştirilir.

Matris matris = new Matris();
matris[0, 0] = 5;
int deger = matris[0, 0];
Console.WriteLine(deger); // Çıktı: 5

Yukarıdaki örnekte, “Matris” sınıfından bir nesne oluşturuldu ve bu nesne üzerinden iki boyutlu indeksleyici kullanılarak matrisin elemanlarına değer atandı ve okundu.

Association between Classes – Class Coupling (Sınıflar Arasındaki İlişki – Sınıf Bağımlılığı)

Sınıflar arasındaki ilişkiler, nesne yönelimli programlamanın temelini oluşturur. Ancak, bu ilişkilerin nasıl yapılandırıldığı ve sınıflar arasındaki bağımlılıkların nasıl yönetildiği önemli bir konsepttir. Bu bölümde, “Class Coupling” yani “Sınıf Bağımlılığı” konusunu derinlemesine inceleyerek, sınıflar arasındaki ilişkilerin etkili bir şekilde nasıl yönetileceğini anlayacağız.

Sınıf Bağımlılığı Nedir?

Sınıf bağımlılığı, bir sınıfın başka bir sınıfa bağımlılığını ifade eder. Yani, bir sınıfın içinde başka bir sınıfın örneklerine veya üyelerine ihtiyaç duyması durumudur. Bu bağımlılık, genellikle bir sınıfın içinde başka bir sınıfın nesnesini oluşturarak veya bu sınıfın üyelerine erişerek gerçekleşir.

Sınıf bağımlılığı, yazılımın esnekliği, bakımı ve genişletilebilirliği üzerinde büyük bir etkiye sahiptir. Doğru bir sınıf bağımlılığı yönetimi, kodunuzun daha modüler, anlaşılır ve bakımı kolay olmasını sağlar.

İdeal Sınıf Bağımlılığı Nasıl Olmalıdır?

  1. Düşük Bağlılık (Low Coupling): Sınıflar arasındaki bağımlılık düşük olmalıdır. Yani, bir sınıfın diğerine olan bağımlılığı minimumda tutulmalıdır.
  2. Yüksek İçselleştirme (High Cohesion): Sınıflar, içerdikleri özellikler ve metotlar açısından birbirleriyle uyumlu ve ilişkilendirilmiş olmalıdır. Yani, bir sınıfın içindeki elemanlar birbirleriyle mantıklı bir şekilde gruplandırılmalıdır.
  3. Bağımlılıkları Azaltma: Sınıflar arasındaki doğrudan bağımlılıklar azaltılmalıdır. Bu, sınıfların birbirlerine mümkün olduğunca bağımsız olmasını sağlar.
  4. Arayüzleri Kullanma: Sınıflar arasındaki bağımlılıkları arayüzler üzerinden yönetmek, sınıfların birbirine daha esnek bir şekilde uyum sağlamasını sağlar. Bu sayede, bir sınıfın iç yapısını değiştirmek diğer sınıfları etkilemez.

Sınıf Bağımlılığını Yönetmek İçin İpuçları:

  1. Bağımlılıkları En Aza İndirgeme: Bir sınıfın başka bir sınıfa olan doğrudan bağımlılığını en aza indirin. Sınıflar arasındaki iletişimi arayüzler üzerinden kurarak bağımlılıkları azaltabilirsiniz.
  2. Dependency Injection Kullanma: Bağımlılıkları sınıfların içinde oluşturmak yerine dışarıdan enjekte etmek, sınıfları daha bağımsız ve test edilebilir hale getirebilir.
  3. Arayüzleri ve Soyutlamayı Kullanma: Arayüzler ve soyut sınıflar, sınıflar arasındaki bağımlılıkları yönetmek için güçlü araçlardır. Bir sınıfın diğer sınıfın arayüzünü veya soyut sınıfını kullanması, bağımlılıkları daha az katı hale getirir.
  4. Mediator Deseni Kullanma: Mediator deseni, sınıflar arasındaki iletişimi bir aracı üzerinden sağlar. Bu, sınıflar arasındaki doğrudan bağımlılıkları azaltabilir.

Örnek Senaryo: Online Alışveriş Uygulaması

Diyelim ki bir online alışveriş uygulaması yazıyorsunuz. Sepet işlemleri, ödeme işlemleri ve envanter yönetimi gibi farklı modüller içeren sınıflarınız var. İdeal bağımlılık yönetimi için:

  • Sepet sınıfının ödeme sınıfına doğrudan bağlı olmaması, aralarında bir arayüz veya soyut sınıf kullanılması.
  • Envanter sınıfının sepet sınıfına doğrudan müdahale etmemesi, gerekirse bir aracı (örneğin, bir sipariş yöneticisi) kullanarak iletişim kurması.

Bu şekilde, her modülün kendi içinde bağımsız olduğu ve sınıf değişikliklerinin diğerlerini etkilemediği bir yapı elde edebilirsiniz.

Sonuç:

Sınıf bağımlılığı yönetimi, yazılım tasarımının önemli bir parçasıdır. Düşük bağımlılık, kodun daha okunabilir, bakımı daha kolay ve genişletilebilir olmasını sağlar. İdeal bağımlılık yönetimi için örnek senaryoları inceleyerek, gerçek projelerde bu prensipleri nasıl uygulayabileceğinizi anlayabilirsiniz.

Bu bölümde sınıf bağımlılığı üzerine odaklanarak, yazılım tasarımında esnek ve sürdürülebilir bir yapı oluşturmanıza yardımcı olacak önemli konuları keşfettik.

Sınıflar Arasındaki İlişki – Kalıtım

Sınıflar arasındaki ilişkilerin önemli bir yönü olan “Inheritance” (Kalıtım), nesne yönelimli programlamada (OOP) sıklıkla kullanılan ve kodun yeniden kullanılabilirliğini artıran güçlü bir kavramdır. Bu bölümde, sınıflar arasındaki kalıtım ilişkisinin nasıl çalıştığını ve nasıl etkili bir şekilde kullanılabileceğini inceleyeceğiz.

Kalıtım Nedir?

Kalıtım, bir sınıfın başka bir sınıftan özelliklerini ve davranışlarını miras alması anlamına gelir. Bu, kodun tekrar kullanılabilirliğini artırır ve kodun daha modüler hale gelmesini sağlar. Kalıtım, “temel sınıf” (base class) veya “ana sınıf” (parent class) ile “türetilmiş sınıf” (derived class) veya “alt sınıf” (child class) arasında gerçekleşir.

// Temel sınıf
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}");
    }
}

// Türetilmiş sınıf
public class Student : Person
{
    public int StudentId { get; set; }
}

Yukarıdaki örnekte, “Person” sınıfı temel sınıf olarak tanımlanmıştır. “Student” sınıfı ise “Person” sınıfından kalıtım alarak, “Person” sınıfının özelliklerini ve davranışlarını miras almıştır.

Temel Kavramlar:

  1. Base Class (Temel Sınıf): Kalıtımı veren sınıfa “temel sınıf” veya “ana sınıf” denir. Yukarıdaki örnekte “Person” sınıfı temel sınıftır.
  2. Derived Class (Türetilmiş Sınıf): Kalıtımı alan sınıfa “türetilmiş sınıf” veya “alt sınıf” denir. Yukarıdaki örnekte “Student” sınıfı türetilmiş sınıftır.
  3. IS-A İlişkisi: Kalıtım, “IS-A” ilişkisi olarak bilinir. Örneğin, “Student IS-A Person” ifadesi doğrudur, çünkü öğrenci bir kişidir.

Kalıtımın Avantajları:

  1. Kodun Yeniden Kullanılabilirliği: Temel sınıftaki özellikler ve metotlar türetilmiş sınıfta tekrar yazılmadan kullanılabilir.
  2. Kodun Modülerliği: Kod, daha küçük ve bağımsız parçalara bölünerek daha anlaşılır ve bakımı kolay hale gelir.
  3. Polimorfizm: Kalıtım, polimorfizmi destekler. Aynı isme sahip metotlar, türetilmiş sınıflarda farklı şekilde uygulanabilir.
  4. Kodun Genişletilebilirliği: Yeni özellikler eklemek veya var olanları değiştirmek, sadece türetilmiş sınıfları etkiler. Temel sınıfta yapılan değişiklikler, türetilmiş sınıfları otomatik olarak etkiler.

Dikkat Edilmesi Gereken Hususlar:

  1. Çoklu Kalıtım: C# dilinde bir sınıf sadece bir tane temel sınıftan kalıtım alabilir (tekil kalıtım). Ancak, birden fazla arayüzü implemente edebilir.
  2. Kapsülleme: Türetilmiş sınıf, temel sınıfın private üyelerine doğrudan erişemez. Ancak, protected üyelere erişim mümkündür.

Polimorfizm ve Kalıtım:

Polimorfizm, kalıtım ile sıkça bir araya gelir. Temel sınıfta tanımlanan bir metot, türetilmiş sınıfta aynı isimle farklı bir şekilde uygulanabilir. Bu durum, aynı isme sahip metotlara farklı davranışlar kazandırmamıza olanak tanır.

// Temel sınıf
public class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing a shape");
    }
}

// Türetilmiş sınıf
public class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}

Yukarıdaki örnekte, “Shape” sınıfında tanımlanan “Draw” metodu türetilmiş “Circle” sınıfında farklı bir şekilde uygulanmıştır.

Sonuç:

Kalıtım, nesne yönelimli programlamanın temel prensiplerinden biridir ve kodun organizasyonunu, yeniden kullanılabilirliğini ve bakımını kolaylaştırır. Bu bölümde, sınıflar arasındaki kalıtım ilişkisini anlamak için temel kavramları inceledik.

Sınıflar Arasındaki İlişki – Kompozisyon

Sınıflar arasındaki ilişkiler, nesne yönelimli programlamanın (OOP) temelini oluşturur ve bu ilişkilerin doğru bir şekilde kurulması, kodunun esnekliğini ve sürdürülebilirliğini artırabilir. Bu bölümde, “Composition” yani “Kompozisyon” kavramını inceleyerek, sınıflar arasındaki güçlü bir ilişki türünü keşfedeceğiz.

Kompozisyon Nedir?

Kompozisyon, bir sınıfın başka bir sınıfın parçalarını içermesi anlamına gelir. Yani, bir sınıf içinde başka bir sınıfın örneklerini kullanarak daha karmaşık bir yapı oluşturulur. Bu, “has-a” ilişkisi olarak bilinir.

// Parça sınıfı
public class Engine
{
    public void Start()
    {
        Console.WriteLine("Engine started");
    }
}

// Bütün sınıfı
public class Car
{
    private Engine engine = new Engine();

    public void Start()
    {
        engine.Start();
        Console.WriteLine("Car started");
    }
}

Yukarıdaki örnekte, “Car” sınıfı, “Engine” sınıfının bir örneğini içerir. “Car” sınıfı, “Engine” sınıfının parçasıdır.

Temel Kavramlar:

  1. Has-A İlişkisi: Kompozisyon, “has-a” ilişkisi olarak da adlandırılır. Örneğin, “Car has an Engine” ifadesi doğrudur.
  2. Parça (Part) ve Bütün (Whole): Kompozisyon, bir sınıfın başka bir sınıfın parçalarını içermesi anlamına gelir. Bu, parça-bütün ilişkisini ifade eder.

Kompozisyonun Avantajları:

  1. Modülerlik: Kompozisyon, kodu modüler hale getirir. Bir sınıfın içindeki parçalar bağımsızdır ve bu da kodun daha bakımı kolay ve anlaşılır olmasını sağlar.
  2. Esneklik: Parçaların bağımsızlığı, kodu daha esnek hale getirir. Bir parça değiştirildiğinde veya güncellendiğinde, bütün sınıfın değiştirilmesi gerekmez.
  3. Tekrar Kullanılabilirlik: Parçaların tekrar kullanılabilirliği artar. Örneğin, farklı sınıfların içinde aynı parçayı kullanabilirsiniz.
  4. Mantıklı Organizasyon: Kompozisyon, nesnelerin mantıklı bir şekilde organize edilmesini sağlar. Her sınıfın belirli bir sorumluluğu vardır ve bu sorumlulukları yerine getirmek için parçalar kullanılır.

Kompozisyonun Dikkat Edilmesi Gereken Noktalar:

  1. Lifetime (Ömür): Parçaların ömrü, bütün sınıfın ömründen farklı olabilir. Bu durumu dikkate alarak parça örneklerini oluşturmalı ve kullanmalısınız.
  2. Sınıf Arasındaki Bağımlılıklar: Kompozisyonun, sınıflar arasındaki bağımlılıkları artırabileceğini unutmayın. Dikkatlice planlanmalı ve gerektiğinde arayüzler kullanılmalıdır.

Örnek Senaryo: Ev ve Mobilya

Diyelim ki, bir ev sınıfı oluşturuyorsunuz ve bu ev içinde mobilya bulunuyor. Ev sınıfı, farklı türde mobilyaları içeren bir kompozisyon ilişkisi kurabilirsiniz. Bu, evin iç yapısını düzenlemenize ve mobilya türlerini daha kolay değiştirmenize olanak tanır.

Sonuç:

Bu bölümde, sınıflar arasındaki ilişkilerin bir türü olan “Composition” (Kompozisyon) konseptini inceledik. Kompozisyon, modüler ve esnek kod yazmanıza yardımcı olabilir.

Sınıflar Arasındaki İlişki – Kalıtım Yerine Kompozisyonu Tercih Etme

Nesne yönelimli programlamada (OOP), sınıflar arasındaki ilişkileri kurarken “Composition over Inheritance” (Kalıtım Yerine Kompozisyonu Tercih Etme) prensibi, sıkça vurgulanan bir tasarım ilkesidir. Bu prensip, kodun daha esnek, sürdürülebilir ve modüler olmasına katkıda bulunabilir.

Neden “Composition over Inheritance” Prensibi?

  1. Esneklik (Flexibility): Kalıtım, sınıflar arasında sıkı bir bağlantı oluşturabilir ve bu da kodun esnekliğini azaltabilir. Kompozisyon, bağımlılıkları minimize eder ve sınıfları birbirinden daha bağımsız hale getirir.
  2. Kodun Tekrar Kullanılabilirliği (Code Reusability): Kalıtım, sınıflar arasında kodun paylaşılmasını sağlar, ancak aynı zamanda bağımlılıkları artırabilir. Kompozisyon, sınıfların içindeki bağımsız parçaları kullanarak tekrar kullanılabilir kod yazmanıza olanak tanır.
  3. Modülerlik (Modularity): Kompozisyon, kodu daha modüler hale getirir. Her bir parça, kendi sorumluluklarına sahip olabilir ve bu parçaları bir araya getirerek daha karmaşık yapıları oluşturabilirsiniz.
  4. Değişikliklere Duyarlılık (Sensitivity to Changes): Kalıtım, temel sınıflarda yapılan değişikliklerin türetilmiş sınıfları etkileme riskini taşır. Kompozisyon, sınıflar arasındaki bağımlılıkları azaltarak değişikliklere daha duyarlı bir tasarım sağlar.

Kompozisyonu Nasıl Kullanabiliriz?

  1. Parça Sınıfları Oluşturun: Kodunuzu küçük, bağımsız ve yeniden kullanılabilir parçalara bölmek için parça sınıfları oluşturun.
  2. Kompozisyon İlişkileri Kurun: Sınıflar arasındaki ilişkileri kurarken, kalıtım yerine kompozisyonu tercih edin. Bir sınıfın içinde başka bir sınıfın örneğini içererek (has-a ilişkisi), daha esnek bir yapı elde edebilirsiniz.
  3. Arayüzleri Kullanın: Arayüzler, kompozisyonu destekler ve sınıflar arasındaki bağımlılıkları daha zayıf hale getirebilir.
public interface IDrawable
{
    void Draw();
}

public class Circle : IDrawable
{
    public void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}

public class Drawing
{
    private IDrawable drawable;

    public Drawing(IDrawable drawable)
    {
        this.drawable = drawable;
    }

    public void Draw()
    {
        drawable.Draw();
    }
}

Yukarıdaki örnekte, “Drawing” sınıfı bir “IDrawable” arayüzü üzerinden çizim yapabilen nesneleri içerir. Bu, çeşitli çizim nesnelerini (Circle, Rectangle, vb.) içerebilen esnek bir yapı sağlar.

  1. Dependency Injection Kullanın: Bağımlılıkları sınıfların içinde oluşturmak yerine dışarıdan enjekte ederek kompozisyonu destekleyin.

Kalıtımın Uygun Olduğu Durumlar:

Kalıtım, hala bazı durumlar için uygun olabilir. Özellikle, “IS-A” ilişkisi açıkça tanımlanabilirse (bir sınıf başka bir sınıfın bir türüdürse), kalıtım kullanılabilir. Ancak, bu durumları iyi düşünmeli ve olası değişikliklere karşı duyarlı bir tasarım oluşturmalısınız.

Inheritance – Access Modifiers (Kalıtım – Erişim Belirleyicileri)

Kalıtım, nesne yönelimli programlamada (OOP) kodun daha modüler ve sürdürülebilir olmasını sağlayan önemli bir kavramdır. Ancak, kalıtımın kullanımı sırasında erişim belirleyicilerinin doğru bir şekilde yönetilmesi, sınıflar arasındaki ilişkilerin sağlıklı bir şekilde kurulmasını sağlar.

Erişim Belirleyicileri Nedir?

Erişim belirleyicileri, sınıfların, alanların, metotların ve diğer üyelerin, başka sınıflar tarafından nasıl erişilebileceğini kontrol eden özelliklerdir. C# dilinde dört temel erişim belirleyici bulunmaktadır:

  1. Public (Genel): “public” belirleyici, bir üyenin herkes tarafından erişilebilir olduğunu gösterir. Yani, bu üye herhangi bir sınıf veya nesne tarafından kullanılabilir.
  2. Private (Özel): “private” belirleyici, bir üyenin sadece kendi sınıfı içinde erişilebilir olduğunu gösterir. Başka hiçbir sınıf bu üyeye doğrudan erişemez.
  3. Protected (Korumalı): “protected” belirleyici, bir üyenin kendi sınıfı ve bu sınıftan türetilmiş sınıflar tarafından erişilebilir olduğunu gösterir.
  4. Internal (İç): “internal” belirleyici, bir üyenin tanımlandığı derleme içinde erişilebilir olduğunu gösterir. Yani, aynı derleme içindeki diğer sınıflar bu üyeye erişebilir, ancak dışarıdan gelen sınıflar erişemez.

Erişim Belirleyicileri ve Kalıtım:

Public Üyeler: Temel sınıfta tanımlanan bir üyenin erişim belirleyicisi “public” ise, bu üye türetilmiş sınıflar tarafından miras alınan sınıflar tarafından da kullanılabilir.

public class Animal
{
    public void Eat()
    {
        Console.WriteLine("Animal is eating");
    }
}

public class Dog : Animal
{
    // Animal sınıfındaki public metodu miras alındı.
}

Protected Üyeler: Eğer temel sınıfta tanımlanan bir üyenin erişim belirleyicisi “protected” ise, bu üye sadece türetilmiş sınıflar tarafından erişilebilir. Bu durum, temel sınıfın davranışını türetilmiş sınıflar tarafından genişletmek için kullanılabilir.

public class Animal
{
    protected void Move()
    {
        Console.WriteLine("Animal is moving");
    }
}

public class Bird : Animal
{
    public void Fly()
    {
        Move(); // Temel sınıftan miras alınan protected metodu kullanma
        Console.WriteLine("Bird is flying");
    }
}

Private Üyeler: Eğer temel sınıfta tanımlanan bir üyenin erişim belirleyicisi “private” ise, bu üye türetilmiş sınıflar tarafından erişilemez. Bu durum, temel sınıfın içsel detaylarını saklamak için kullanılır.

public class Person
{
    private string name;

    public void SetName(string newName)
    {
        name = newName;
    }
}

public class Employee : Person
{
    // Burada "name" private olduğu için doğrudan erişim mümkün değildir.
}

Erişim Belirleyicileri ve Constructorlar:

Erişim belirleyicileri, sınıflar arasındaki kalıtım ilişkisinde constructorlar üzerinde de etkili olabilir. Özellikle, temel sınıftaki constructor’ların türetilmiş sınıflar tarafından nasıl kullanılabileceğini dikkate almak önemlidir.

public class Animal
{
    // Public constructor
    public Animal()
    {
        Console.WriteLine("Animal is created");
    }

    // Protected constructor
    protected Animal(string name)
    {
        Console.WriteLine($"Animal named {name} is created");
    }
}

public class Dog : Animal
{
    // Dog sınıfı, temel sınıftaki public constructor'ı kullanabilir.
    public Dog() : base()
    {
        Console.WriteLine("Dog is created");
    }

    // Dog sınıfı, temel sınıftaki protected constructor'ı kullanabilir.
    public Dog(string name) : base(name)
    {
        Console.WriteLine($"Dog named {name} is created");
    }
}

Erişim Belirleyicilerinin Kullanımında Dikkat Edilmesi Gerekenler:

  1. Abstraction (Soyutlama): Kalıtım ve erişim belirleyicileri kullanılırken soyutlama ilkesine dikkat edilmelidir. Temel sınıfın iç detayları, türetilmiş sınıflar için gizlenmelidir.
  2. Open/Closed Principle: Open/Closed Prensibi, bir sınıfın genişletilebilir, ancak değiştirilemez olması ilkesini savunur. Erişim belirleyicileri, bu prensibi uygulamak için önemli bir araçtır.
  3. Interface Kullanımı: Arayüzler, bir sınıfın genişlemesi için kalıtım yerine kullanılabilir. Bu durumda, erişim belirleyicileri daha az önemli olabilir çünkü arayüzler genellikle açık ve public’tir.

Sonuç:

Erişim belirleyicileri, kalıtım ilişkilerini düzenlerken önemli bir rol oynar. Doğru erişim belirleyicilerini kullanmak, kodun daha güvenli, sürdürülebilir ve modüler olmasını sağlar. Bu bölümde, kalıtım ve erişim belirleyicileri arasındaki ilişkiyi anlamaya odaklandık.

Inheritance – Constructors and Inheritance (Kalıtım – Constructorlar ve Kalıtım)

Kalıtım, nesne yönelimli programlamanın (OOP) temel prensiplerinden biridir ve sınıflar arasındaki ilişkilerin kurulmasında önemli bir rol oynar. Ancak, kalıtımın kullanımı sırasında constructorlar üzerinde dikkatli olunması gerekir. Bu bölümde, constructorların kalıtım ilişkilerinde nasıl çalıştığını ve dikkate alınması gereken bazı önemli konuları inceleyeceğiz.

Temel Constructor Kavramları:

Base Class (Temel Sınıf) Constructor:

  • Bir sınıfın constructor’ı, sınıfın bir örneği oluşturulduğunda çağrılır.
  • Temel sınıfın constructor’ı, türetilmiş sınıfın constructor’ından önce çağrılır.
public class Animal
{
    public Animal()
    {
        Console.WriteLine("Animal constructor called");
    }
}

public class Dog : Animal
{
    public Dog()
    {
        Console.WriteLine("Dog constructor called");
    }
}

// Kullanım:
Dog myDog = new Dog();

ukarıdaki örnekte, Dog sınıfı bir Animal sınıfından kalıtım aldığı için, Dog sınıfının bir örneği oluşturulduğunda önce Animal sınıfının constructor’ı çağrılır, ardından Dog sınıfının constructor’ı çağrılır.

Constructor Parametreleri:

  • Temel sınıfın constructor’ı parametre alıyorsa, türetilmiş sınıfın constructor’ı da aynı parametreleri almalı ve bu parametreleri temel sınıfın constructor’ına iletmelidir.
public class Animal
{
    public Animal(string name)
    {
        Console.WriteLine($"Animal named {name} created");
    }
}

public class Dog : Animal
{
    public Dog(string name) : base(name)
    {
        Console.WriteLine($"Dog named {name} created");
    }
}

// Kullanım:
Dog myDog = new Dog("Buddy");

Yukarıdaki örnekte, Dog sınıfının constructor’ı, Animal sınıfının constructor’ına name parametresini ileterek çağrılır.

Constructorların Çalışma Sırası:

Constructorlar, kalıtım zinciri boyunca yukarıdan aşağıya doğru çağrılır. Yani, en üstteki temel sınıftan başlayarak türetilmiş sınıflara kadar sırayla constructorlar çağrılır.

public class Animal
{
    public Animal()
    {
        Console.WriteLine("Animal constructor called");
    }
}

public class Mammal : Animal
{
    public Mammal()
    {
        Console.WriteLine("Mammal constructor called");
    }
}

public class Dog : Mammal
{
    public Dog()
    {
        Console.WriteLine("Dog constructor called");
    }
}

// Kullanım:
Dog myDog = new Dog();

Yukarıdaki örnekte, Dog sınıfı bir Mammal sınıfından, Mammal sınıfı da bir Animal sınıfından kalıtım aldığı için constructorlar AnimalMammalDog sırasında çağrılır.

Dikkate Alınması Gereken Hususlar:

Base Constructor Çağrımı:

  • Türetilmiş sınıfın constructor’ı içinde base anahtar kelimesi kullanılarak temel sınıfın constructor’ı çağrılmalıdır.
  • base anahtar kelimesi, türetilmiş sınıfın içinde sadece constructor içinde kullanılabilir.
public class Animal
{
    public Animal(string name)
    {
        Console.WriteLine($"Animal named {name} created");
    }
}

public class Dog : Animal
{
    public Dog(string name) : base(name)
    {
        Console.WriteLine($"Dog named {name} created");
    }
}

Virtual ve Override:

  • Temel sınıftaki bir method veya property, türetilmiş sınıfta değiştirilecekse, temel sınıftaki method veya property virtual olarak işaretlenmeli ve türetilmiş sınıfta override edilmelidir.
public class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("Animal speaks");
    }
}

public class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Dog barks");
    }
}

Static Constructor:

  • Kalıtım zinciri boyunca static constructorlar çağrılmaz. Static constructor, yalnızca sınıfın bir örneği oluşturulduğunda çağrılır.
public class Animal
{
    static Animal()
    {
        Console.WriteLine("Static Animal constructor called");
    }

    public Animal()
    {
        Console.WriteLine("Instance Animal constructor called");
    }
}

public class Dog : Animal
{
    static Dog()
    {
        Console.WriteLine("Static Dog constructor called");
    }

    public Dog()
    {
        Console.WriteLine("Instance Dog constructor called");
    }
}

// Kullanım:
Dog myDog = new Dog();

Yukarıdaki örnekte, Dog sınıfının bir örneği oluşturulduğunda Static Dog constructor ve Instance Dog constructor sırasıyla çağrılır. Ancak, Animal sınıfının static constructor’ı çağrılmaz.

Constructorlar ve Base Anahtar Kelimesi Kullanımı:

Base anahtar kelimesi, türetilmiş sınıfın constructor’ı içinde temel sınıfın constructor’ını çağırmak için kullanılır. Bu, temel sınıfın özelliklerinin başlangıç değerlerini ayarlamak veya temel sınıfa özel işlemleri gerçekleştirmek için önemlidir.

public class Animal
{
    private string name;

    public Animal(string name)
    {
        this.name = name;
        Console.WriteLine($"Animal named {name} created");
    }
}

public class Dog : Animal
{
    private int age;

    public Dog(string name, int age) : base(name)
    {
        this.age = age;
        Console.WriteLine($"Dog named {name} with age {age} created");
    }
}

// Kullanım:
Dog myDog = new Dog("Buddy", 3);

Yukarıdaki örnekte, Dog sınıfının constructor’ı, Animal sınıfının constructor’ını base(name) kullanarak çağırır ve temel sınıfın özelliklerini başlangıç değerleriyle ayarlar.

Constructorlar ve Overloading:

Sınıflar arasındaki kalıtım ilişkisi sırasında, her sınıf kendi constructor’larını tanımlayabilir ve overloading yapabilir. Ancak, temel sınıfın bir constructor’ı çağrıldığında, bu constructor’ın temel sınıftaki bir constructor’ı tarafından çağrılmış olması önemlidir.

public class Animal
{
    public Animal()
    {
        Console.WriteLine("Animal constructor called");
    }

    public Animal(string name)
    {
        Console.WriteLine($"Animal named {name} created");
    }
}

public class Dog : Animal
{
    public Dog()
    {
        Console.WriteLine("Dog constructor called");
    }

    public Dog(string name) : base(name)
    {
        Console.WriteLine($"Dog named {name} created");
    }
}

// Kullanım:
Dog myDog1 = new Dog();
Dog myDog2 = new Dog("Buddy");

Yukarıdaki örnekte, Dog sınıfının iki farklı constructor’ı bulunmaktadır. İlk örnekte Dog sınıfının parametresiz constructor’ı çağrıldığında, önce Animal sınıfının parametresiz constructor’ı çağrılır. İkinci örnekte ise Dog sınıfının parametreli constructor’ı çağrıldığında, bu sefer Animal sınıfının parametreli constructor’ı çağrılır.

Sonuç:

Constructorlar ve kalıtım, nesne yönelimli programlamanın önemli konseptlerindendir. Constructorların doğru bir şekilde kullanılması, sınıflar arasındaki kalıtım ilişkilerini güçlendirir ve temel sınıftan türetilmiş sınıfların başlangıç durumlarını düzenler.

Inheritance – Upcasting and Downcasting (Kalıtım – Yukarı Dönüşüm ve Aşağı Dönüşüm)

Kalıtım, nesne yönelimli programlamanın (OOP) temel prensiplerinden biri olan bir konsepttir. Kalıtımın bir sonucu olarak, sınıflar arasında tür dönüşümleri gerçekleştirmek mümkündür. Bu dönüşümler genellikle “Upcasting” (Yukarı Dönüşüm) ve “Downcasting” (Aşağı Dönüşüm) olarak adlandırılır. Bu bölümde, bu iki tür dönüşümü derinlemesine anlayacağız.

Upcasting (Yukarı Dönüşüm):

Nedir? Upcasting, bir alt sınıfın referansını, üst sınıfın referans tipine çevirmektir. Yani, daha özel bir tip olan alt sınıf türündeki bir nesnenin referansı, genel bir üst sınıf türüne dönüştürülür.

Neden Kullanılır?

  1. Genel bir sınıf referansı kullanmak, kodun daha esnek ve genişletilebilir olmasını sağlar.
  2. İleri düzey tasarımlarda ve polimorfizm (çok biçimlilik) kullanımında sıklıkla karşımıza çıkar.

Örnek:

public class Animal
{
    public void Eat()
    {
        Console.WriteLine("Animal is eating");
    }
}

public class Dog : Animal
{
    public void Bark()
    {
        Console.WriteLine("Dog is barking");
    }
}

// Upcasting
Dog myDog = new Dog();
Animal animalReference = myDog; // Dog tipindeki nesnenin referansını Animal tipine çevirme

animalReference.Eat(); // Animal sınıfının metotlarına erişim mümkündür
// animalReference.Bark(); // Bu satır hata verir, çünkü Animal tipindeki referans Dog sınıfının özel bir metotuna erişemez

Yukarıdaki örnekte, myDog değişkeni bir Dog sınıfının örneğini temsil eder. Ardından, myDog değişkeni Animal tipine çevrilerek (Upcasting) animalReference adlı bir referans ile kullanılır. animalReference artık Dog sınıfının özel metotlarına erişemez, ancak Animal sınıfının metotlarına erişim mümkündür.

Downcasting (Aşağı Dönüşüm):

Nedir? Downcasting, bir üst sınıf referansını, alt sınıfın referans tipine çevirmektir. Yani, daha genel bir üst sınıf türündeki bir nesnenin referansı, daha özel bir alt sınıf türüne dönüştürülür.

Neden Kullanılır?

  1. Upcasting’in tersine, özel bir sınıfın özel metotlarına veya özelliklerine erişmek için kullanılır.
  2. Daha genel bir referansla başlayan bir işlemi, daha spesifik bir işlemle devam ettirmek için kullanılır.

Örnek:

public class Animal
{
    public void Eat()
    {
        Console.WriteLine("Animal is eating");
    }
}

public class Dog : Animal
{
    public void Bark()
    {
        Console.WriteLine("Dog is barking");
    }
}

// Upcasting
Dog myDog = new Dog();
Animal animalReference = myDog; // Dog tipindeki nesnenin referansını Animal tipine çevirme

// Downcasting
if (animalReference is Dog)
{
    Dog castedDog = (Dog)animalReference; // Animal tipindeki referansı Dog tipine çevirme
    castedDog.Bark(); // Dog sınıfının özel metotlarına erişim mümkündür
}

Yukarıdaki örnekte, animalReference değişkeni bir Animal sınıfının referansını temsil eder. Ardından, is anahtar kelimesi ile kontrol edilerek animalReference değişkeni bir Dog sınıfı referansına (Downcasting) çevrilir ve daha sonra Dog sınıfının özel metotlarına erişim mümkün olur.

Dikkat Edilmesi Gerekenler:

  1. Downcasting Güvenli Değildir:
    • Downcasting işlemi, çoğu durumda güvenli değildir ve runtime’da hatalara neden olabilir. Bu nedenle, downcasting yapmadan önce tip kontrolü (type checking) yapılması önemlidir (is anahtar kelimesi kullanılabilir).
  2. As ve is Anahtar Kelimeleri:
    • as anahtar kelimesi, bir nesnenin belirli bir türe dönüştürülüp dönüştürülemeyeceğini kontrol ederken, is anahtar kelimesi sadece bu kontrolü yapar.
// as kullanımı
Dog castedDog = animalReference as Dog;
if (castedDog != null)
{
    castedDog.Bark();
}

// is kullanımı
if (animalReference is Dog)
{
    Dog castedDog = (Dog)animalReference;
    castedDog.Bark();
}

Sonuç:

Upcasting ve downcasting, kalıtım ilişkilerinde tür dönüşümlerini gerçekleştirmek için kullanılır. Upcasting, genel bir referansla başlayıp daha özel bir referansla devam etmeyi sağlar. Downcasting ise daha genel bir referansı, daha özel bir referansa dönüştürerek özel sınıfın özelliklerine erişmeyi mümkün kılar. Ancak, downcasting işlemlerinde dikkatli olunmalı ve tip güvenliği sağlanmalıdır.

Inheritance – Boxing and Unboxing (Kalıtım – Boxing ve Unboxing)

Kalıtımın yanı sıra, C# dilinde değer türleri ve referans türleri arasında dönüşümler gerçekleştirebileceğimiz iki önemli konsept vardır: Boxing ve Unboxing. Bu iki işlem, değer türlerini (int, double, char vb.) referans türlerine (object, interface, delegate vb.) dönüştürmek ve tam tersini gerçekleştirmek için kullanılır.

Boxing (Değer Türünden Referans Türüne Dönüşüm):

Nedir? Boxing, değer türlerini bir obje türüne (object) dönüştürmektir. Değer türleri, örneğin int, double gibi türler, genellikle değer tipi oldukları için stack bellek bölgesinde depolanır. Ancak, bir değer tipi bir referans türüne dönüştürülürse, bu işleme “boxing” denir ve değer tipi heap bellek bölgesinde bir nesne olarak depolanır.

Neden Kullanılır?

  1. Değer türlerini koleksiyonlara eklemek istediğimizde (ArrayList, List<object> gibi).
  2. Değer türlerini, genel bir nesne referansı olarak kullanmak istediğimizde.

Örnek:

int intValue = 42;

// Boxing
object boxedValue = intValue; // Değer türü (int) referans türüne (object) dönüştürüldü

Unboxing (Referans Türünden Değer Türüne Dönüşüm):

Nedir? Unboxing, bir obje türünden (object) bir değer türüne dönüştürmektir. Bir değer tipi, obje türünden çıkarılarak (unboxing) tekrar değer tipine dönüştürülür ve stack bellek bölgesine yerleştirilir.

Neden Kullanılır?

  1. Bir obje türündeki bir değeri, asıl değer tipine geri dönüştürmek istediğimizde.
  2. Genel bir nesne referansı içinde depolanan değeri asıl değer tipine çıkarmak istediğimizde.

Örnek:

object boxedValue = 42;

// Unboxing
int intValue = (int)boxedValue; // Referans türü (object) değer türüne (int) dönüştürüldü

Dikkat Edilmesi Gereken Hususlar:

  1. Performans Etkisi:
    • Boxing ve unboxing işlemleri, performansı olumsuz etkileyebilir. Değer türleri genellikle stack bellek bölgesinde hızlı erişim sağlarken, heap bellek bölgesinde depolanan referans türlerinin erişimi daha maliyetli olabilir.
  2. Tip Güvenliği:
    • Boxing ve unboxing işlemleri, tip güvenliği açısından risklidir. Çünkü runtime sırasında tür uyumsuzluklarından kaynaklanan hatalara neden olabilir.
  3. Nullable Değer Tipleri:
    • Nullable (Nullable<T>) değer tipleri, değer türlerini nullable türlerine dönüştürmekte özel bir durumu temsil eder.
int? nullableInt = intValue;      // Implicit conversion (Boxing)
int unboxedValue = (int)nullableInt; // Explicit conversion (Unboxing)
  1. Casting Operatörleri:
    • Boxing ve unboxing işlemleri, casting operatörleri ((type)) kullanılarak gerçekleştirilir.
  2. Null Kontrolü:
    • Unboxing işlemi yapmadan önce, referansın null olup olmadığını kontrol etmek önemlidir.
object boxedValue = null;

// Unboxing with null check
if (boxedValue != null && boxedValue is int)
{
    int intValue = (int)boxedValue;
}

Boxing ve Unboxing Örnek Senaryosu:

List<object> mixedList = new List<object>();
mixedList.Add(42); // Boxing (int to object)
mixedList.Add("Hello"); // Boxing (string to object)

// Unboxing (object to int)
foreach (object item in mixedList)
{
    if (item is int)
    {
        int intValue = (int)item;
        Console.WriteLine($"Unboxed int value: {intValue}");
    }
    else
    {
        Console.WriteLine($"Non-int value: {item}");
    }
}

Yukarıdaki örnekte, bir List<object> koleksiyonuna hem bir int değeri hem de bir string değeri eklenmiştir. Daha sonra bu koleksiyon üzerinde dolaşarak, her elemanın türüne göre unboxing işlemi gerçekleştirilmiştir.

Sonuç:

Boxing ve unboxing, değer türleri ile referans türleri arasında dönüşümler yapmamıza olanak tanır. Ancak, bu işlemler performans ve tip güvenliği açısından dikkatli bir şekilde kullanılmalıdır. Boxing ve unboxing işlemlerini minimumda tutarak, kodunuzun daha etkin ve güvenli olmasını sağlayabilirsiniz.

Inheritance – Exercises (Kalıtım – Alıştırmalar)

Kalıtım konseptini anlamak ve bu konuda ustalaşmak için aşağıda önerilen birkaç alıştırma bulunmaktadır. Bu alıştırmalar, kalıtımın farklı yönlerini ve kullanım durumlarını anlamanıza yardımcı olacaktır.

Alıştırma 1: Geometrik Şekiller

Base Class (Temel Sınıf) Oluşturun:

  • Shape adında bir temel sınıf oluşturun. Bu sınıfın içinde geometrik şekillerin alanını hesaplamak için bir metot bulunsun.
public class Shape
{
    // Alanı hesaplamak için bir metot
    public virtual double CalculateArea()
    {
        return 0;
    }
}

Türetilmiş Sınıflar Oluşturun:

  • Circle ve Rectangle adında Shape sınıfından türetilmiş sınıflar oluşturun. Her iki sınıf da alanı hesaplamak için temel sınıfın metotunu geçersiz kılsın.
public class Circle : Shape
{
    // Circle sınıfına özgü özellikler ve alan hesaplama
}

public class Rectangle : Shape
{
    // Rectangle sınıfına özgü özellikler ve alan hesaplama
}

Kullanım:

  • Oluşturduğunuz sınıfları kullanarak bir Circle ve bir Rectangle örneği oluşturun. Ardından, her birinin alanını hesaplayarak ekrana yazdırın.
Circle circle = new Circle(/* gerekli parametreler */);
Rectangle rectangle = new Rectangle(/* gerekli parametreler */);

double circleArea = circle.CalculateArea();
double rectangleArea = rectangle.CalculateArea();

Console.WriteLine($"Circle Area: {circleArea}");
Console.WriteLine($"Rectangle Area: {rectangleArea}");

Alıştırma 2: Personel Yönetimi

Base Class (Temel Sınıf) Oluşturun:

  • Person adında bir temel sınıf oluşturun. Bu sınıfın içinde bir kişinin adını ve yaşını tutan özellikler bulunsun.
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Türetilmiş Sınıflar Oluşturun:

  • Employee ve Manager adında Person sınıfından türetilmiş sınıflar oluşturun. Her iki sınıf da çalışanların görevlerini ve maaşlarını tutan özelliklere sahip olsu
public class Employee : Person
{
    // Employee sınıfına özgü özellikler (görev, maaş vb.)
}

public class Manager : Employee
{
    // Manager sınıfına özgü özellikler (ek görevler, prim vb.)
}

Kullanım:

  • Oluşturduğunuz sınıfları kullanarak bir Employee ve bir Manager örneği oluşturun. Her birinin özelliklerini ayarlayın ve ekrana yazdırın.
Employee employee = new Employee();
Manager manager = new Manager();

employee.Name = "John Doe";
employee.Age = 25;
employee.Position = "Software Developer";
employee.Salary = 50000;

manager.Name = "Jane Smith";
manager.Age = 35;
manager.Position = "Project Manager";
manager.Salary = 80000;
manager.Bonus = 10000;

Console.WriteLine($"Employee: {employee.Name}, Age: {employee.Age}, Position: {employee.Position}, Salary: {employee.Salary}");
Console.WriteLine($"Manager: {manager.Name}, Age: {manager.Age}, Position: {manager.Position}, Salary: {manager.Salary}, Bonus: {manager.Bonus}");

Bu alıştırmalar, kalıtım konseptini pekiştirmenize yardımcı olacaktır. Oluşturduğunuz sınıfları genişleterek ve farklı durumları ele alarak daha karmaşık senaryoları deneyebilirsiniz.

Polymorphism – Method Overriding (Çok Biçimlilik – Metot Geçersiz Kılma)

Çok biçimlilik, nesne yönelimli programlamanın (OOP) önemli bir konseptidir ve metot geçersiz kılma (method overriding), çok biçimliliği gerçekleştirmek için kullanılan bir tekniktir.

Metot Geçersiz Kılma Nedir?

Metot geçersiz kılma, türetilmiş bir sınıfın, temel sınıfta (üst sınıf) tanımlanan bir metodu kendi ihtiyacına göre yeniden tanımlamasıdır. Yani, temel sınıfta bulunan bir metodu türetilmiş sınıf, aynı isim ve imza ile tekrar tanımlayarak değiştirebilir.

Neden Metot Geçersiz Kılma Kullanılır?

  1. Özel Davranış:
    • Türetilmiş sınıflar, temel sınıftan miras aldıkları metotları kendi ihtiyaçlarına uygun özel davranışlarla değiştirebilirler.
  2. Çok Biçimlilik:
    • Aynı isme sahip metotların farklı davranışlara sahip olması, çok biçimliliği (polymorphism) sağlar. Bu da aynı türden farklı nesnelerin aynı arayüzle kullanılabilmesini sağlar.

Nasıl Metot Geçersiz Kılınır?

Override Anahtar Kelimesi:

  • Türetilmiş sınıfta geçersiz kılma işlemi gerçekleştirmek için override anahtar kelimesi kullanılır. Bu, derleyiciye bu metodu temel sınıfta bulunan bir metodu geçersiz kılacak şekilde uyarır.
public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Animal makes a sound");
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Dog barks");
    }
}

Base Keyword (Base Anahtar Kelimesi):

  • Türetilmiş sınıfta geçersiz kılma yapılırken, temel sınıfta bulunan aynı isimdeki metodu çağırmak için base anahtar kelimesi kullanılabilir.
public class Dog : Animal
{
    public override void MakeSound()
    {
        base.MakeSound(); // Temel sınıftaki metodu çağır
        Console.WriteLine("Dog barks");
    }
}

Kullanım Örneği:

Animal animal = new Dog();
animal.MakeSound(); // Dog barks

Yukarıdaki örnekte, Dog sınıfı, Animal sınıfından türetilmiştir ve MakeSound metodu geçersiz kılınmıştır. Bu sayede, Animal tipinde bir referansın MakeSound metodunu çağırdığımızda, runtime sırasında gerçek tür olan Dog sınıfının MakeSound metodu çalıştırılır. Bu da çok biçimliliğin bir örneğidir.

Dikkat Edilmesi Gereken Hususlar:

  1. Implements vs. Overrides:
    • Bir metodu geçersiz kılmak, sadece temel sınıftan miras almakla aynı şey değildir. Temel sınıftaki bir metodu geçersiz kılarken, imzasını (isim ve parametreler) doğru bir şekilde kullanmalısınız.
  2. Virtual Metotlar:
    • Bir metodu geçersiz kılabilmeniz için, temel sınıfta bu metodu virtual olarak işaretlemiş olmanız gerekir. Aksi takdirde geçersiz kılma işlemi yapılamaz.
  3. Abstraction ile İlişkisi:
    • Metot geçersiz kılma, soyut sınıflar (abstract class) ve soyut metotlar (abstract method) ile sıkça kullanılır. Soyut sınıflar, türetilmiş sınıfların belirli metotları uygulamalarını zorlayarak bu tür çok biçimliliği sağlar.

Sonuç:

Metot geçersiz kılma, çok biçimliliği sağlayan önemli bir OOP konseptidir. Türetilmiş sınıfların temel sınıftan miras aldıkları metotları kendi ihtiyaçlarına göre uyarlamalarını sağlar ve aynı arayüzle farklı davranışlar sergilemelerini mümkün kılar.

Polymorphism – Abstract Classes and Members (Çok Biçimlilik – Soyut Sınıflar ve Üyeler)

Soyut sınıflar (abstract classes) ve soyut üyeler (abstract members), çok biçimliliği destekleyen önemli OOP (Nesne Yönelimli Programlama) kavramlarıdır.

Soyut Sınıflar Nedir?

Soyut sınıflar, tamamlanmış bir sınıfın temelini oluşturan ancak tam olarak uygulanmamış veya tamamlanmamış metotları içeren sınıflardır. Soyut sınıflar, genellikle ortak özellikleri ve davranışları içeren sınıflar için bir çerçeve sağlamak amacıyla kullanılır.

Soyut Üyeler Nedir?

Soyut üyeler, soyut sınıflarda tanımlanan ve alt sınıflar tarafından uygulanması gereken metotları, özellikleri veya endeksleri ifade eder. Soyut üyeler, sınıfın alt sınıfları tarafından tamamlanmalıdır.

Soyut Sınıf ve Soyut Üye Tanımlama:

public abstract class Shape
{
    // Soyut metot
    public abstract double CalculateArea();

    // Normal metot
    public void Display()
    {
        Console.WriteLine("Displaying shape");
    }

    // Soyut özellik
    public abstract string Name { get; set; }
}

public class Circle : Shape
{
    // Soyut metotun implementasyonu
    public override double CalculateArea()
    {
        // Alan hesaplama mantığı burada uygulanır
        return 0;
    }

    // Soyut özelliğin implementasyonu
    private string _name;
    public override string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

Soyut Sınıf ve Soyut Üye Kullanımı:

Shape shape = new Circle();
shape.Display();         // Normal metot çağrılır
double area = shape.CalculateArea();  // Soyut metot çağrılır (Circle sınıfındaki implementasyon çalışır)
shape.Name = "My Circle"; // Soyut özellik kullanılır

Soyut Sınıf ve Soyut Üye Avantajları:

  1. Ortak Davranışları Tanımlama:
    • Soyut sınıflar, bir grup sınıf arasında paylaşılan ortak davranışları tanımlamak için kullanılır. Örneğin, geometrik şekillerin alanını hesaplayan bir soyut sınıf, farklı şekil sınıfları tarafından uygulanan bir metot içerebilir.
  2. Çok Biçimlilik (Polymorphism):
    • Soyut sınıflar, türetilmiş sınıflar arasında çok biçimliliği destekler. Aynı soyut sınıf tipinden türetilmiş farklı sınıflar, aynı arayüzle kullanılabilir.
  3. Zorunlu Metot ve Özellikler:
    • Soyut üyeler, türetilmiş sınıflar tarafından uygulanmak zorundadır. Bu, belirli bir soyut sınıfın alt sınıflarının belirli metotları veya özellikleri uygulamasını sağlar.

Soyut Sınıf ve Soyut Üye Dikkat Edilmesi Gereken Hususlar:

Soyut Sınıfın Kendi İnstance’ı Oluşturulamaz:

  • Soyut sınıflardan doğrudan örnek (instance) oluşturulamaz. Ancak, türetilmiş sınıfların örnekleri oluşturulabilir.
// Hatalı kullanım:
// Shape shape = new Shape(); // Hata! Soyut sınıfın örneği oluşturulamaz

// Doğru kullanım:
Shape circle = new Circle(); // Türetilmiş sınıfın örneği oluşturulabilir

Soyut Metotların Gövdesi Yoktur:

  • Soyut metotlar sadece imza sağlar, gövde (implementation) içermez. Bu nedenle, alt sınıflar tarafından uygulanmalıdır.

Türetilmiş Sınıflarda İmplementasyon Zorunluluğu:

  • Soyut sınıflardaki soyut üyeler, türetilmiş sınıflar tarafından implemente edilmelidir. Aksi takdirde derleme hatası alınır.
// Hatalı kullanım:
public class InvalidShape : Shape
{
    // Hata! Soyut üyeleri implemente etmek zorunludur
}

Sonuç:

Soyut sınıflar ve soyut üyeler, çok biçimliliği destekleyen ve ortak davranışları tanımlamak için kullanılan güçlü bir OOP aracıdır. Soyut sınıflar, bir grup sınıf arasında ortak davranışları paylaşmanın yanı sıra, alt sınıfların belirli metotları veya özellikleri uygulamasını zorlayarak güvenli bir tasarım sağlar.

Polymorphism – Sealed Classes and Members (Çok Biçimlilik – Mühürlü Sınıflar ve Üyeler)

Mühürlü sınıflar ve üyeler, C# dilinde kalıtım (inheritance) ve çok biçimlilik (polymorphism) kullanımını sınırlayan önemli bir kavramdır. Bu bölümde, neden ve nasıl mühürlü sınıflar ve üyeler kullanıldığını, bu konseptlerin avantajlarını ve dikkat edilmesi gereken noktaları derinlemesine inceleyeceğiz.

Mühürlü (Sealed) Sınıflar Nedir?

Mühürlü sınıflar, türetilmiş sınıfların bu sınıflardan daha fazla türetilmesini engelleyen sınıflardır. Yani, bir sınıf mühürlendiyse, bu sınıftan türetilen başka bir sınıf oluşturulamaz.

Mühürlü Üyeler Nedir?

Mühürlü üyeler, sınıf içinde tanımlanan metotları, özellikleri veya alanları, türetilmiş sınıflar tarafından mühürlenerek (override edilemeyerek) kullanılmalarını sağlayan üyelerdir.

Mühürlü Sınıf ve Üye Tanımlama:

public sealed class FinalClass
{
    // Mühürlü metot
    public sealed override void Display()
    {
        Console.WriteLine("Displaying from FinalClass");
    }

    // Mühürlü özellik
    public sealed string Name { get; set; }

    // Mühürlü alan
    public sealed int Id;
}

Mühürlü Sınıf ve Üye Kullanımı:

public class DerivedClass : FinalClass // Hata! Mühürlü sınıftan türetilmez
{
    // Hata! Mühürlü metot mühürlenemez
    public override void Display()
    {
        Console.WriteLine("Displaying from DerivedClass");
    }

    // Hata! Mühürlü özellik mühürlenemez
    public override string Name { get; set; }

    // Hata! Mühürlü alan mühürlenemez
    public override int Id;
}

Mühürlü Sınıf ve Üye Avantajları:

  1. Güvenlik ve Sabitlik:
    • Mühürlü sınıflar ve üyeler, kodun belirli bir durumu sabit ve güvenli bir şekilde korumasına yardımcı olur. Bu, beklenmeyen değişikliklerin önüne geçer.
  2. Performans İyileştirmeleri:
    • Mühürlü sınıflar, derleyicilere daha fazla optimizasyon yapma fırsatı tanır çünkü bu sınıflardan türetilen alt sınıfların oluşturulamayacağı bilindiği için belirli durumlar için özel optimizasyonlar gerçekleştirilebilir.
  3. API Kararlılığı:
    • Bir API tasarlanırken, bazı sınıfların veya üyelerin değiştirilemez veya mühürlenmiş olması, API’nin kullanıcılarına kararlı bir deneyim sunar ve geriye dönük uyumluluk sağlar.

Mühürlü Sınıf ve Üye Dikkat Edilmesi Gereken Hususlar:

  1. Doğru Yerlerde Kullanım:
    • Mühürlü sınıflar ve üyeler, dikkatlice seçilmelidir. Her sınıf veya üye mühürlü olmamalıdır. Yalnızca güvenliğin ve performansın gerçekten iyileştirilmesi gereken durumlarda kullanılmalıdır.
  2. Mühürlü Metotların Virtual Olması:
    • Eğer bir metot mühürlenecekse (sealed anahtar kelimesi kullanılacaksa), bu metotun önceki sınıflar tarafından virtual olarak işaretlenmiş olması gerekir.
public class BaseClass
{
    public virtual sealed void Display()
    {
        Console.WriteLine("Displaying from BaseClass");
    }
}

Sonuç:

Mühürlü sınıflar ve üyeler, C# dilinde belirli durumlarda kullanılan önemli bir konsepttir. Bu kavramlar, kodun güvenliğini artırabilir, performansı iyileştirebilir ve API kararlılığını sağlayabilir. Ancak, dikkatlice kullanılmalıdır, aksi takdirde tasarım esnekliğini azaltabilir.

Interfaces – What is an Interface? (Arayüzler – Arayüz Nedir?)

Arayüzler (interfaces), C# programlamasında çok biçimliliği desteklemek, sınıflar arasında bir arayüz sağlamak ve kodun daha geniş bir uygulama yelpazesiyle uyumlu olmasını sağlamak için kullanılan önemli bir OOP (Nesne Yönelimli Programlama) kavramıdır.

Arayüz Nedir?

Arayüz, bir sınıfın veya bir yapı (struct) türünün uygulaması gereken bir kontrattır. Yani, bir arayüz, içinde bulunan metotları, özellikleri ve diğer üyeleri tanımlar, ancak kendi başına bir somut (concrete) implementasyona sahip değildir. Arayüzler, sınıflar arasında belirli bir davranışın paylaşılmasını sağlar.

Arayüz Tanımlama:

public interface ILogger
{
    // Metot tanımı
    void LogMessage(string message);

    // Özellik tanımı
    int LogCount { get; }

    // Metot ve özelliklerin yanı sıra etkinlikler, endeksler vb. de tanımlanabilir
}

Arayüz Kullanımı:

public class ConsoleLogger : ILogger
{
    private int logCount;

    // ILogger arayüzünden gelen metotların implementasyonu
    public void LogMessage(string message)
    {
        Console.WriteLine($"Logging message: {message}");
        logCount++;
    }

    // ILogger arayüzünden gelen özelliğin implementasyonu
    public int LogCount
    {
        get { return logCount; }
    }
}

Arayüz Avantajları:

  1. Çok Biçimlilik (Polymorphism):
    • Arayüzler, sınıflar arasında çok biçimliliği sağlar. Farklı sınıflar aynı arayüzü uyguladığında, aynı arayüzle çağrılabilirler.
  2. Sınıflar Arası Bağlantı:
    • Arayüzler, sınıflar arasında bir tür bağlantı sağlar. Farklı sınıfların aynı arayüzü uygulamaları, bu sınıfları birbirine benzer hale getirir.
  3. Kodun Daha Modüler Olması:
    • Arayüzler, kodun daha modüler ve yeniden kullanılabilir olmasını sağlar. Bir arayüzü uygulayan sınıflar, arayüzün tanımladığı davranışları içermek zorundadır, bu da kodun daha anlaşılır ve bakımı daha kolay olmasını sağlar.

Arayüz Dikkat Edilmesi Gereken Hususlar:

  1. İsimlendirme Konvansiyonları:
    • Arayüz isimleri genellikle “I” harfi ile başlar. Örneğin, ILogger gibi.
  2. Bir Sınıf Birden Fazla Arayüzü Uygulayabilir:
    • Bir sınıf, birden fazla arayüzü aynı anda uygulayabilir. Bu durum, farklı davranışları bir arada bulundurmak için kullanışlıdır.
public class DatabaseLogger : ILogger, IErrorLogger
{
    // ILogger ve IErrorLogger arayüzlerinden gelen metotların implementasyonu
    // ...
}

Arayüzlerde Her Şey Public’tir:

  • Arayüzlerde tanımlanan tüm üyeler (metotlar, özellikler, endeksler vb.) varsayılan olarak publictir. Bu nedenle, arayüzü uygulayan sınıfların bu üyeleri public olarak implemente etmeleri gerekir.

Sonuç:

Arayüzler, çok biçimlilik sağlayarak, sınıflar arasında bağlantı kurarak ve kodun daha modüler olmasını sağlayarak C# dilinde güçlü bir programlama kavramıdır. Arayüzler, yazılım tasarımını esnek ve genişletilebilir hale getirmek için önemli bir araçtır.

Interfaces and Testability (Arayüzler ve Test Edilebilirlik)

Arayüzler (interfaces), yazılım testlerini oluştururken ve yürütürken büyük bir avantaj sağlayabilir. Bu bölümde, arayüzlerin test edilebilir yazılımların tasarımında nasıl kullanılabileceğini ve bu konunun neden önemli olduğunu derinlemesine ele alacağız.

Test Edilebilir Yazılım Nedir?

Test edilebilir yazılım, bir yazılımın belirli koşullar altında doğru sonuçları üretebilme yeteneği olarak tanımlanır. Test edilebilir yazılım tasarımı, yazılımın her bir bileşeninin, sınıfın veya modülünün bağımsız olarak test edilebilmesini ve bu testlerin güvenilir bir şekilde sonuçlanabilmesini amaçlar.

Arayüzlerin Rolü:

  1. Bağımlılıkları Azaltma:
    • Arayüzler, bağımlılıkları azaltmak için kullanılabilir. Bir sınıf, bağımlılıklarını arayüzler aracılığıyla yönetirse, bu sınıfın bağımlılıkları daha kolay bir şekilde taklit edilebilir ve yerine geçilebilir hale gelir.
  2. Mock (Taklit) Nesnelerle Çalışma:
    • Testlerde kullanılmak üzere mock (taklit) nesneler oluşturmak, gerçek nesneleri taklit ederek testlerin daha kontrol edilebilir olmasını sağlar. Arayüzler, bu taklit nesnelerin oluşturulmasını ve testlerin daha etkili bir şekilde yürütülmesini sağlar.

Örnek Senaryo:

Diyelim ki, bir uygulama içinde bir veritabanına erişim sağlayan bir DatabaseManager sınıfımız var. Bu sınıfın test edilebilir olması için arayüzlerin nasıl kullanılacağını inceleyelim:

public interface IDatabaseService
{
    void SaveData(string data);
    string RetrieveData();
}

public class DatabaseManager : IDatabaseService
{
    public void SaveData(string data)
    {
        // Veriyi kaydetme işlemi
    }

    public string RetrieveData()
    {
        // Veriyi geri alma işlemi
        return "Mock Data";
    }
}

Yukarıdaki örnekte, DatabaseManager sınıfı, IDatabaseService arayüzünü uygular. Bu, sınıfın veritabanı işlemlerini gerçekleştiren metotları içermesini sağlar. Ancak, testlerde bu sınıfın yerine geçecek bir mock nesne oluşturabiliriz:

public class MockDatabaseService : IDatabaseService
{
    public void SaveData(string data)
    {
        // Mock veri kaydetme işlemi
    }

    public string RetrieveData()
    {
        return "Mock Data";
    }
}

Test senaryosunda, MockDatabaseService sınıfını kullanarak, gerçek bir veritabanı yerine taklit bir veritabanı kullanabiliriz. Bu sayede, testlerimiz bağımsız ve tekrarlanabilir olur.

Avantajlar:

  1. Modülerlik ve Esneklik:
    • Arayüzler, sınıfları ve modülleri birbirinden bağımsız hale getirerek modüler ve esnek bir tasarım sağlar.
  2. Test Senaryoları Oluşturma Kolaylığı:
    • Arayüzler sayesinde, mock nesneler oluşturarak test senaryolarını daha kolay bir şekilde oluşturabiliriz.
  3. Bağımlılıkları Azaltma:
    • Arayüzler, bağımlılıkları azaltarak sınıfları birbirinden bağımsız hale getirir ve bu sayede daha test edilebilir bir yazılım tasarımı sağlar.

Dikkat Edilmesi Gereken Hususlar:

  1. Doğru Arayüzleri Seçme:
    • Test edilebilirlik için doğru arayüzleri seçmek önemlidir. Her sınıf için ayrı bir arayüz oluşturmak yerine, genel ve işlevsel arayüzleri kullanmak daha mantıklı olabilir.
  2. Kod Tekrarını Önleme:
    • Fazla sayıda arayüz kullanmak, kod tekrarına neden olabilir. Bu nedenle, gereksiz arayüzleri önlemek ve gerektiğinde ortak arayüzleri tercih etmek önemlidir.

Sonuç:

Arayüzler, test edilebilir yazılım tasarımında önemli bir rol oynar. Yazılım geliştirme sürecinde bağımlılıkları azaltmak, mock nesnelerle çalışmak ve test senaryolarını oluşturmak için arayüzleri etkili bir şekilde kullanmak, yazılımın daha güvenilir ve sürdürülebilir olmasını sağlar.

Interfaces are NOT for Multiple Inheritance (Arayüzler Çoklu Miras İçin Değildir)

C# dilinde, arayüzlerin çoklu miras (multiple inheritance) için kullanılmaması, tasarım prensiplerini koruma ve kodun karmaşıklığını azaltma amacını taşır.

Çoklu Mirasın Sorunları:

Elmas Şeklindeki Miras Problemi:

  • Çoklu mirasın bir sorunu, elmas şeklindeki miras problemidir. Bu durumda, bir sınıf, iki farklı üst sınıftan türetilirken, bu üst sınıflar aynı alt sınıfı miras alıyorsa karışıklıklar ve uyumsuzluklar ortaya çıkabilir.
class A { }
class B : A { }
class C : A { }
class D : B, C { } // Elmas şeklindeki miras

Kod Karmaşıklığı ve Bakım Zorluğu:

  • Çoklu miras, kodun karmaşıklığını artırabilir ve bakımını zorlaştırabilir. Birden fazla sınıftan miras almak, sınıflar arasında karmaşık ilişkilerin oluşmasına neden olabilir.

Makul ve Anlamlı Hiyerarşiler Oluşturamama:

  • Çoklu miras, sınıf hiyerarşilerinin anlamsız ve karmaşık olmasına neden olabilir. Bu da kodun anlaşılmasını zorlaştırabilir.

Arayüzlerin Rolü:

  1. Bağımsızlık ve Modülerlik:
    • Arayüzler, bağımsızlık ve modülerlik sağlar. Bir sınıf birden fazla arayüzü uygulayabilir, ancak bu durumda sınıfın içindeki ilişkiler daha açık ve anlamlı olur.
  2. Karmaşıklığı Azaltma:
    • Arayüzler, karmaşıklığı azaltmak için kullanılır. Her arayüz, belirli bir kontratı temsil eder ve sınıflar sadece ihtiyaç duydukları arayüzleri uygularlar.

Neden Arayüzler Çoklu Miras İçin Kullanılmaz?

  1. Çelişen İsim Çakışmaları:
    • Birden fazla arayüz aynı isimde bir metodu veya özelliği tanımlarsa, bu durumda çakışan isimlerle başa çıkmak zordur.
  2. Tek Bir Implementasyon Sorunu:
    • Çoklu mirasın sağlıklı bir şekilde uygulanabilmesi için bir sınıfın birden fazla sınıftan gelen metotları tek bir implementasyonla birleştirmesi gerekir ki bu da zorlayıcı bir durumdur.
  3. Makul ve Anlamlı Hiyerarşiler Zorluğu:
    • Çoklu miras, makul ve anlamlı sınıf hiyerarşileri oluşturmayı zorlaştırabilir. Her sınıfın çok sayıda üst sınıfa miras alması, kodu karmaşık hale getirebilir.

Doğru Kullanım:

Arayüzlerin amacı, sınıflar arasında ortak davranışları tanımlamak ve uygulamaktır. Bir sınıf, ihtiyaç duyduğu arayüzleri uygulayarak gereksinimlerini karşılar. Bu durum, sınıflar arasında sıkı bir bağımlılık olmadan modüler ve bağımsız bir tasarımı destekler.

public interface IDrawable
{
    void Draw();
}

public interface ILoggable
{
    void Log();
}

public class Circle : IDrawable
{
    public void Draw()
    {
        // Çemberi çizme işlemi
    }
}

Sonuç:

Arayüzler, çoklu mirasın getirdiği karmaşıklığı ortadan kaldırmak ve modüler tasarımlar oluşturmak için kullanılır. Arayüzler, sınıflar arasında bağımsızlık ve esneklik sağlar, bu da yazılımın daha sürdürülebilir ve anlaşılır olmasını sağlar.

Interfaces and Polymorphism (Arayüzler ve Çok Biçimlilik)

Arayüzler (interfaces), C# dilinde çok biçimliliği desteklemek için güçlü bir araçtır. Bu bölümde, arayüzlerin çok biçimlilikle nasıl birleştirildiğini, bu birleşmenin neden önemli olduğunu ve nasıl etkili bir şekilde kullanılabileceğini derinlemesine inceleyeceğiz.

Çok Biçimlilik Nedir?

Çok biçimlilik (polymorphism), bir nesnenin farklı şekillerde davranabilme yeteneğini ifade eder. C# dilinde, çok biçimlilik genellikle kalıtım (inheritance) ve arayüzler (interfaces) aracılığıyla sağlanır.

Arayüzlerin Çok Biçimlilikte Rolü:

  1. Çok Biçimlilik için Temel İlke:
    • Arayüzler, çok biçimliliğin temelini oluşturur. Bir sınıf, bir arayüzü uygularsa, bu sınıf, o arayüzü uygulayan diğer sınıflarla aynı arabirimle çalışabilir.
  2. Arayüzleri Uygulamak:
    • Bir sınıf, bir arayüzü uygularsa, o sınıfın tüm arayüz metotlarını gerçekleştirmesi (implemente etmesi) gerekir. Bu da o sınıfın, arayüzün tanımladığı davranışları desteklediği anlamına gelir.
public interface IShape
{
    void Draw();
}

public class Circle : IShape
{
    public void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}

public class Rectangle : IShape
{
    public void Draw()
    {
        Console.WriteLine("Drawing a rectangle");
    }
}

Yukarıdaki örnekte, Circle ve Rectangle sınıfları, aynı IShape arayüzünü uygular. Her iki sınıf da Draw metotunu gerçekleştirir, ancak içerikleri farklıdır.

Arayüzler ve Çok Biçimliliğin Avantajları:

  1. Genişletilebilirlik:
    • Yeni sınıflar eklenirken, var olan arayüzlerin uygulanması sayesinde kod genişletilebilir. Yeni sınıflar, mevcut kodla aynı arabirimle çalışabilir.
  2. Bakım Kolaylığı:
    • Kodun bakımı, arayüzlerin kullanılması sayesinde kolaylaşır. Çünkü bir arayüzü uygulayan bir sınıf, arayüzün tanımladığı davranışları içermek zorundadır.
  3. Modüler Tasarım:
    • Arayüzler, modüler tasarımı destekler. Her arayüz, belirli bir davranışı temsil eder ve bu davranışı uygulayan sınıflar birbirinin yerine geçebilir.

Arayüzlerin Çok Biçimlilikle Kullanımı:

Çoklu Arayüz İmplementasyonu:

  • Bir sınıf, birden fazla arayüzü uygulayabilir. Bu durumda, sınıfın hem birinci hem de ikinci arayüzle ilişkilendirilmiş davranışları içermesi gerekir.
public interface IRenderable
{
    void Render();
}

public class Square : IShape, IRenderable
{
    public void Draw()
    {
        Console.WriteLine("Drawing a square");
    }

    public void Render()
    {
        Console.WriteLine("Rendering a square");
    }
}

Arayüz Referansları ile Çalışma:

  • Arayüz referansları, bir nesnenin hangi arayüzü uyguladığına bakılmaksızın bu nesne ile çalışmayı sağlar.
IShape shape = new Circle();
shape.Draw(); // Çemberin çizimi yapılır

shape = new Rectangle();
shape.Draw(); // Dikdörtgenin çizimi yapılır

Dikkat Edilmesi Gereken Hususlar:

  1. Her Arayüz Bir Davranışı Temsil Etmelidir:
    • Her arayüz, belirli bir davranışı temsil etmelidir. Arayüzler, sınıflar arasında ortak bir kontrat sağlar.
  2. Kodu Mantıklı ve Anlaşılır Kılma:
    • Arayüzleri, kodu daha mantıklı ve anlaşılır hale getirmek için kullanmak önemlidir. Her arayüz, bir sınıfın belirli bir kontratı yerine getirmesini sağlar.
  3. SOLID İlkelerine Uygun Tasarım:
    • Arayüzleri kullanırken, SOLID prensiplerine uygun bir tasarım hedeflenmelidir. Bu prensipler, yazılım tasarımının sürdürülebilir ve esnek olmasını sağlar.

Sonuç:

Arayüzler, C# dilinde çok biçimliliği desteklemek için güçlü bir araçtır. İyi tasarlanmış arayüzler, genişletilebilir, bakımı kolay ve modüler bir kod tabanı oluşturmanın anahtarıdır.

Bir şeye başlayıp başarısız olmaktan daha kötü tek şey hiçbir şeye başlamamaktır.

Seth Godin

Bir sonraki yazıda görüşmek dileğiyle!”

Leave a Reply

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir


2 + 10 = ?