Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
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.
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, 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, 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?
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ı
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.
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.
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ı
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.
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ı
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.
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.
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.
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ı
Ö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.
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ı
İ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.
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?
Sınıf Bağımlılığını Yönetmek İçin İpuçları:
Ö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:
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 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:
Kalıtımın Avantajları:
Dikkat Edilmesi Gereken Hususlar:
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 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:
Kompozisyonun Avantajları:
Kompozisyonun Dikkat Edilmesi Gereken Noktalar:
Ö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.
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?
Kompozisyonu Nasıl Kullanabiliriz?
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.
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.
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:
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:
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.
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:
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:
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 Animal
→ Mammal
→ Dog
sırasında çağrılır.
Dikkate Alınması Gereken Hususlar:
Base Constructor Çağrımı:
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:
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:
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.
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.
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?
Ö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.
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?
Ö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.
is
anahtar kelimesi kullanılabilir).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.
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.
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?
Örnek:
int intValue = 42;
// Boxing
object boxedValue = intValue; // Değer türü (int) referans türüne (object) dönüştürüldü
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?
Örnek:
object boxedValue = 42;
// Unboxing
int intValue = (int)boxedValue; // Referans türü (object) değer türüne (int) dönüştürüldü
int? nullableInt = intValue; // Implicit conversion (Boxing)
int unboxedValue = (int)nullableInt; // Explicit conversion (Unboxing)
(type)
) kullanılarak gerçekleştirilir.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.
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.
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:
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}");
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 olsupublic 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:
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.
Ç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, 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.
Override Anahtar Kelimesi:
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):
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.
virtual
olarak işaretlemiş olmanız gerekir. Aksi takdirde geçersiz kılma işlemi yapılamaz.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.
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, 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, 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.
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ın Kendi İnstance’ı Oluşturulamaz:
// 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:
Türetilmiş Sınıflarda İmplementasyon Zorunluluğu:
// Hatalı kullanım:
public class InvalidShape : Shape
{
// Hata! Soyut üyeleri implemente etmek zorunludur
}
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.
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ü 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, 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.
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;
}
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");
}
}
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.
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, 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.
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
}
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; }
}
}
ILogger
gibi.public class DatabaseLogger : ILogger, IErrorLogger
{
// ILogger ve IErrorLogger arayüzlerinden gelen metotların implementasyonu
// ...
}
Arayüzlerde Her Şey Public’tir:
metotlar
, özellikler
, endeksler
vb.) varsayılan olarak public
tir. Bu nedenle, arayüzü uygulayan sınıfların bu üyeleri public
olarak implemente etmeleri gerekir.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.
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, 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.
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.
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.
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.
Elmas Şeklindeki Miras Problemi:
class A { }
class B : A { }
class C : A { }
class D : B, C { } // Elmas şeklindeki miras
Kod Karmaşıklığı ve Bakım Zorluğu:
Makul ve Anlamlı Hiyerarşiler Oluşturamama:
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
}
}
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.
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 (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.
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.
Çoklu Arayüz İmplementasyonu:
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:
IShape shape = new Circle();
shape.Draw(); // Çemberin çizimi yapılır
shape = new Rectangle();
shape.Draw(); // Dikdörtgenin çizimi yapılır
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 sonraki yazıda görüşmek dileğiyle!”