Polimorfizm Nedir?
Polimorfizm, esasında kalıtım gibi biyolojik bir terimdir. Yazılımda polimorfizm, iki veya daha fazla farklı nesnenin aynı türden sınıflar tarafından referans alması anlamına gelir.
OOP’de (Nesne Yönelimli Programlama) polimorfizm, iki ya da daha fazla nesnenin aynı tür sınıf tarafından karşılanabilmesi veya referans edilebilmesi anlamına gelir. Bir başka deyişle, bir nesnenin birden fazla farklı türdeki referans tarafından işaretlenebilmesi polimorfizmdir.
Polimorfizm, OOP tasarımlarında geliştirilen koda daha esnek ve manevra kabiliyeti yüksek nitelikler kazandıran bir özelliktir. Bu sayede, farklı türdeki nesnelerle aynı türde işlem yapabilmek mümkün hale gelir ve kod yeniden kullanılabilirlik ile genişletilebilirlik açısından daha güçlü hale gelir.
Bir Nesnenin Birden Fazla Referansla İşaretlenmesi Neye Yarar?
Bir nesnenin birden fazla referansla işaretlenmesi, o nesnenin birden fazla türün davranışını sergilemesini sağlar. Bu durum, farklı türdeki referanslarla aynı nesne üzerinde çeşitli işlemler yapılabilmesine imkan tanır ve esneklik kazandırır.
Avantajları:
- Esneklik: Nesneler, farklı türlerdeki referanslarla işaretlenerek çeşitli şekillerde kullanılabilirler.
- Kodun Yeniden Kullanılabilirliği: Aynı nesne, farklı referanslar aracılığıyla farklı bağlamlarda kullanılabilir, bu da kodun yeniden kullanılabilirliğini artırır.
- Genişletilebilirlik: Yeni türler eklemek ve bu türlerin mevcut sistemde nasıl kullanılacağını tanımlamak daha kolay hale gelir.
- Çok Biçimlilik (Polimorfizm): Aynı işlem, farklı türlerdeki nesneler üzerinde çalıştırılabilir, bu da kodun daha genel ve esnek olmasını sağlar.
Polimorfizm Felsefesi – 1
Kuş deyince aklınıza ne geliyor?
Farkındaysanız, kuş deyince belki de aklınıza herhangi belirli bir tür gelmeyebilir, ancak özünde hepsi bir kuş olduğunu görebiliriz. İşte bu hayvanların kendi türlerinin dışında ortak olarak “kuş” diye nitelendirilmeleri polimorfizmdir. Yani, bir olguyu çoklu (poli) form (morfizmos) olarak tanımlayabilmekteyiz.
“Kuş” cinsinden olan tüm hayvanlar, kendi türlerinin dışında bir yandan “kuş” olarak nitelendirilirler. Bu durumda “kuş” kavramı, farklı türlerdeki hayvanları ortak bir şekilde tanımlamaktadır.
Ortak atadan gelen, kalıtımsal olarak “kuş”tan türeyen tüm hayvanlar kendi türleri ya da “Kuş” türü ile referans edilebilirler.
Buradan da şunu anlıyoruz ki, yazılımsal açıdan çok biçimlilik söz konusu olabilmesi için teknik olarak “Kalıtım” olması gerekmektedir. Bunun daha detaylı açıklamasını dersin devamında göreceğiz.
Polimorfizim felsefesi – 2
Araçlar ve Polimorfizm
Araç deyince aklınıza ne geliyor? Otomobil, kamyon, motosiklet veya bisiklet gibi farklı araç türleri olabilir. Farkındaysanız, araç deyince bu taşıtlardan herhangi biri aklınıza gelmeyebilir, ancak özünde bunların hepsi birer araçtır. İşte bu taşıtların kendi türlerinin dışında ortak olarak “araç” diye nitelendirilmeleri polimorfizmdir. Yani, bir olguyu çoklu (poli) form (morfizmos) olarak tanımlayabilmekteyiz.
artık yavaştan programlamada geçelim.
Programlamada Polimorfizm Nerede Kullanılır?
Programlamada polimorfizm, aslında en başından itibaren kullanılmaktadır. Örneğin, elimizdeki herhangi bir byte
türündeki veriyi ister byte
istersek byte
‘dan büyük olan herhangi bir türde tutmak çok biçimliliktir.
Ancak, çok biçimlilik denildiğinde, temel programlamadaki bu durumlardan ziyade nesne tabanlı programlamadaki getirileri önemlidir.
Polimorfizmin Kullanım Alanları
Nesne tabanlı programlamada polimorfizmi uygulamak için türler arasında kalıtımsal bir ilişki olması gerekmektedir. Yani, bir nesnenin başka bir nesne ile işaretlenebilmesi veya referans edilmesi için arada kalıtımsal bir bağ olmalıdır.
Başka bir deyişle, nesne tabanlı programlamada polimorfizm uygulamak istiyorsanız, türler arasında kalıtım uygulanmış olmalıdır. Aksi durumda polimorfizm uygulanması mümkün değildir.
Örnek: Polimorfizmin Kullanımı
Aşağıdaki örnek, polimorfizmin nesne tabanlı programlamada nasıl kullanıldığını göstermektedir:
public class Hayvan
{
public virtual void SesCikar()
{
Console.WriteLine("Hayvan sesi çıkarıyor.");
}
}
public class Kopek : Hayvan
{
public override void SesCikar()
{
Console.WriteLine("Hav Hav");
}
}
public class Kedi : Hayvan
{
public override void SesCikar()
{
Console.WriteLine("Miyav Miyav");
}
}
public class Program
{
public static void Main()
{
Hayvan hayvan1 = new Kopek();
Hayvan hayvan2 = new Kedi();
hayvan1.SesCikar(); // Hav Hav
hayvan2.SesCikar(); // Miyav Miyav
}
}
Bu örnekte, Hayvan
sınıfı temel bir sınıf olarak tanımlanmış ve SesCikar
metodu sanal (virtual
) olarak işaretlenmiştir. Kopek
ve Kedi
sınıfları, Hayvan
sınıfından türemiş ve SesCikar
metodunu kendi türlerine özgü bir şekilde geçersiz kılmışlardır (override
).
Programın Main
metodunda, Hayvan
türünde referanslarla Kopek
ve Kedi
nesneleri işaretlenmiştir. Bu, polimorfizmin bir örneğidir. Çünkü farklı türdeki nesneler (Kopek
, Kedi
), aynı temel tür (Hayvan
) ile referans edilerek aynı metodun (SesCikar
) farklı biçimlerde çalıştırılmasını sağlamaktadır.
Polimorfizm kalıtım ilişkisi
Bir nesneyi, kendi türünün dışındaki bir tür ile işaretleyebilmek veya referans edebilmek için, ilgili nesnenin o türden türetilmiş olması gerekmektedir. Bu, kalıtım ilişkisinin temelini oluşturur ve polimorfizmin uygulanabilmesini sağlar.
namespace polimorfizm
{
class Program
{
static void Main(string[] args)
{
A a = new B(); // A türünden bir referansla B nesnesi
B b = new B(); // B türünden bir referansla B nesnesi
C c = new B(); // C türünden bir referansla B nesnesi
}
}
class A : C
{
}
class B : A
{
}
class C
{
}
}
Açıklama:
Açıklama
C
sınıfı temel bir sınıftır.A
sınıfı,C
sınıfından türemiştir.B
sınıfı iseA
sınıfından türemiştir.
Bu yapıda, B
sınıfı, A
ve C
sınıflarından türetilmiştir, dolayısıyla B
nesnesi, A
ve C
türündeki referanslarla işaretlenebilir.
Kodda Polimorfizm
A a = new B();
ifadesi,A
türündeki bir referanslaB
nesnesini işaretler. Bu,B
nesnesininA
türünden bir nesne gibi davranabileceğini gösterir.B b = new B();
ifadesi,B
türündeki bir referanslaB
nesnesini işaretler.C c = new B();
ifadesi,C
türündeki bir referanslaB
nesnesini işaretler. Bu,B
nesnesininC
türünden bir nesne gibi davranabileceğini gösterir.
Örnek


işte erkek nesnesinin insan referansıyla işaretlenebilmesi çok biçimlilik(Polimorfizm) dir.
Peki, Polimorfizm bir nesneye yönetiminde neye yarar?
Bir nesnenin birden fazla referansla işaretlenmesi, o nesnenin farklı türlerin davranışlarını sergilemesini sağlar. Bu durum, polimorfizmin temel prensiplerinden biridir ve nesne tabanlı programlamada önemli bir esneklik ve genişletilebilirlik sağlar.
Polimorfizm Türleri
1. static Polimorfizm(Compile-Time Polymorphism):
- Metot overloading ile gerçekleştirilir.
- Aynı isimde fakat farklı parametre listelerine sahip birden fazla metot tanımlanır.
- Hangi metotun çağrılacağı derleme zamanında belirlenir.
- Derleme zamanında karar verilmesi nedeniyle performans açısından daha hızlıdır.
using System;
class Calculator
{
// İki integer parametre alan metot
public static int Add(int a, int b)
{
return a + b;
}
// İki double parametre alan metot
public static double Add(double a, double b)
{
return a + b;
}
}
class Program
{
static void Main(string[] args)
{
int sum1 = Calculator.Add(5, 3); // int tipindeki metot çağrılır
double sum2 = Calculator.Add(2.5, 3.7); // double tipindeki metot çağrılır
Console.WriteLine("Sum1: " + sum1); // Çıktı: Sum1: 8
Console.WriteLine("Sum2: " + sum2); // Çıktı: Sum2: 6.2
}
}
Bu örnekte, Calculator
sınıfında iki farklı metot tanımlanmıştır. Her ikisi de Add
ismini taşımaktadır, ancak farklı parametre listelerine sahiptirler (birisi int
, diğeri double
parametre alır). Hangi metotun çağrılacağı, argümanların türlerine göre derleme zamanında belirlenir.
2. Dinamik Polimorfizm (Run-Time Polymorphism):
- Metot override ile gerçekleştirilir.
- Temel sınıfta
virtual
olarak işaretlenmiş metotlar, alt sınıflardaoverride
edilerek yeniden yazılır. - Hangi metotun çalışacağı çalışma zamanında belirlenir.
- Çalışma zamanında karar verilmesi nedeniyle daha esnek ve genişletilebilirdir.
using System;
class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal makes a sound");
}
}
class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Cat meows");
}
}
class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Dog barks");
}
}
class Program
{
static void Main(string[] args)
{
Animal animal1 = new Cat(); // Cat sınıfından türetilmiş nesne, Animal referansıyla işaretlenir
Animal animal2 = new Dog(); // Dog sınıfından türetilmiş nesne, Animal referansıyla işaretlenir
animal1.MakeSound(); // Çıktı: Cat meows
animal2.MakeSound(); // Çıktı: Dog barks
}
}
Bu örnekte, Animal
sınıfında MakeSound
adında bir sanal metot tanımlanmıştır. Bu metot, alt sınıflar tarafından (Cat
ve Dog
) override
edilerek yeniden yazılmıştır. Hangi metotun çağrılacağı, çalışma zamanında referansın gösterdiği nesnenin türüne bağlı olarak belirlenir. Bu nedenle, MakeSound
metodu çalışma zamanında dinamik olarak seçilir.
Bu türlerin her biri, farklı durumlarda kullanılarak kodunuzun esnekliğini ve genişletilebilirliğini artırabilir. Statik polimorfizm, derleme zamanında performans avantajları sağlarken, dinamik polimorfizm ise çalışma zamanında esneklik ve genişletilebilirlik sağlar. Her ikisi de nesne tabanlı programlamanın güçlü araçlarıdır ve doğru senaryolarda kullanıldığında kodunuzu daha okunaklı, anlaşılır ve bakımı kolay hale getirebilir.
Polimorfizm durumlarında tür dönüşümleri
tür dönüşümleri polimorfizm durumlarında oldukça önemlidir. Bu dönüşümler, bir nesnenin kalıtımsal olarak ataları olan referanslar tarafından işaretlenmesini ve bu nesnenin farklı türlere dönüştürülmesini sağlar.
Örneğin, elimizdeki C sınıfı B sınıfından kalıtım alır ve B sınıfı da A sınıfından kalıtım alır.
A a = new C();


Bu durumda, a
referansı bir C türünden nesneyi işaretler, ancak A türünden bir referansla tutulur. İhtiyaç durumunda, bu referans A’nın ataları olan diğer referans türlerine veya kendi türüne dönüştürülebilir:
C c = (C)a;
Bu dönüşümde, a
referansındaki C türünden nesne, kendi türünde bir referansla işaretlenir. Bu tür dönüşümü sağlamak için cast operatörü kullanılır. Aynı şekilde, aşağıdaki gibi de bir dönüşüm yapılabilir:
C c = new C();
A a = c; // veya A a = (A)c;
Bu durumda, c
nesnesi doğrudan A türünden bir referansla işaretlenir. Burada da cast operatörü kullanılabilir, ancak çoğu durumda zaten alt sınıftan üst sınıfa otomatik bir tür dönüşümü gerçekleşir.
Polimorfizm durumlarında üst bir referansla işaretlenen bir nesne, kendi türünden bir referansla işaretlenmek için cast operatörü kullanılarak tür dönüşümü gerçekleştirilir. Bu durum, object türünde gerçekleştirilen Unboxing’e benzer bir mekanizma izler.
Object türünde gerçekleşen Unboxing işlemi, bir object türünden değer tip bir türe dönüşümüdür. Benzer şekilde, polimorfizm durumlarında yapılan tür dönüşümleri de bir üst sınıf referansından alt sınıf referansına dönüşümü ifade eder. Bu dönüşümler, nesne tabanlı programlamadaki genişletilebilirliği ve esnekliği destekler.
Dolayısıyla, object türünde gerçekleştirilen Unboxing işlemi, polimorfizmin bir sonucudur ve bir nesnenin farklı türlere dönüştürülmesini sağlayarak programın daha dinamik hale gelmesine katkıda bulunur.
Boxing ve Unboxing, değer türleri ile referans türleri arasında dönüşüm yapmayı sağlayan işlemlerdir. Bu işlemler genellikle C# gibi dillerde kullanılır. Detaylı anlatımı aşşağıda anlatıyorum.
Boxing (Kutulama):
- Boxing işlemi, bir değer tipini (int, double, vb.) bir nesne türüne (object, interface, vb.) dönüştürme işlemidir.
- Değer tipinin değeri, bellekte heap’te yeni bir alan oluşturularak saklanır ve referans tipi bir değişkene atanır.
- Bu işlem genellikle polymorphism (çok biçimlilik) durumlarında, yani farklı türlerin aynı arayüzü paylaştığı durumlarda kullanılır.
int i = 10;
object obj = i; // Boxing işlemi
Unboxing (Kutudan Çıkarma):
- Unboxing işlemi, bir nesne türünü (object, interface, vb.) bir değer türüne (int, double, vb.) dönüştürme işlemidir.
- Kutulanmış (boxed) bir nesnenin değeri, doğrudan bir değer türüne atanarak alınır.
- Bu işlem, bir önceki kutulama işlemiyle elde edilen değerin orijinal değer tipine geri dönüştürülmesini sağlar.
object obj = 10; // Boxing işlemi
int i = (int)obj; // Unboxing işlemi
Polimorfizm Durumlarında Tür Dönüşümleri
polimorfizm durumlarında tür dönüşümleri gerçekleştirmek için cast
veya as
operatörleri kullanılabilir. Bu operatörler, bir nesneyi farklı türlere dönüştürmek için kullanılır ve her birinin kendine özgü kullanımı ve avantajları vardır.
Cast operatörü
Cast operatörü, üst türden alt türe kalıtımsal ilişkide dönüşüm sağlar. Bu işlem, bir nesnenin türetilmiş bir türden olduğunu belirtir ve bu türle ilgili işlemleri yapmanıza olanak tanır.
class A
{
public void MethodA() => Console.WriteLine("Method in A");
}
class B : A
{
public void MethodB() => Console.WriteLine("Method in B");
}
// Nesne oluşturma ve dönüşüm
A a = new B();
B b = (B)a;
b.MethodB(); // Output: Method in B
Cast Operatörünün Kullanım Kuralları
Kalıtımsal İlişki Olması Gerekliliği:
- Eğer bir nesne, kalıtımsal ilişkisi olmayan bir türe dönüştürülmeye çalışılırsa derleyici hatası alırsınız.
- Örneğin, aşağıdaki kod derleme hatası verecektir çünkü
A
veC
arasında kalıtımsal bir ilişki yoktur:
class A { }
class C { }
A a = new A();
C c = (C)a; // Derleyici hatası
Fiziksel Nesnenin Hiyerarşik Altında Olma Durumu:
- Eğer bir nesne, kalıtımsal ilişkide olup fiziksel olarak hiyerarşik altında olmayan bir türe dönüştürülmeye çalışılırsa, run time hatası (InvalidCastException) alınır.
- Örneğin, aşağıdaki kod run time hatası verecektir çünkü
a
referansı aslındaA
türündendir veB
türüne dönüştürülmeye çalışılmaktadır:
class A { }
class B : A { }
A a = new A();
B b = (B)a; // Run time hatası: InvalidCastException
as Operatörü
as
operatörü, kalıtımsal ilişki olan türler arasında referans dönüşümü yapmamızı sağlayan bir operatördür. as
operatörü, cast operatörüne benzer şekilde çalışır ancak başarısız dönüşümler durumunda run time hatası yerine null
döner.
class A
{
public void MethodA() => Console.WriteLine("Method in A");
}
class B : A
{
public void MethodB() => Console.WriteLine("Method in B");
}
// Nesne oluşturma ve dönüşüm
A a = new B();
B b = a as B;
if (b != null)
{
b.MethodB(); // Output: Method in B
}
else
{
Console.WriteLine("Dönüşüm başarısız.");
}
cast
ve as
Operatörlerinin Farkları
cast
operatörü: Başarısız dönüşümler durumundaInvalidCastException
fırlatır.as
operatörü: Başarısız dönüşümler durumundanull
döner.
Bu farklar, dönüşüm işlemlerinde hata kontrolünü ve hata yönetimini farklı şekillerde ele almamızı sağlar. as
operatörü, dönüşümün başarısız olma ihtimaline karşı daha güvenli bir yaklaşım sunar, çünkü run time hatası yerine null
döner. Bu, özellikle dönüşümün başarısız olma olasılığının yüksek olduğu durumlarda tercih edilebilir.
is Operatörü
is
operatörü, kalıtsal ilişkiye sahip nesnelerin polimorfizm özelliğine nazaran fiziksel olarak hangi türde olduğunu belirlemeye yarayan bir operatördür. is
operatörü, bir nesnenin belirtilen türe uygun olup olmadığını kontrol eder ve sonucunda true
veya false
değerini döndürür.
- True Döner: Eğer nesne belirtilen türe uygun ise.
- False Döner: Eğer nesne belirtilen türe uygun değilse veya kalıtsal bir ilişki yoksa.
Bu operatör sayesinde, polimorfizm uygulanmış bir nesnenin, ihtiyaç doğrultusunda (uygun olan) farklı bir türe dönüştürülmesi için önce is
kontrolü yapılabilir. Ardından, tür dönüşümü için Cast
veya as
operatörleri kullanılabilir. Bu yaklaşım, tür dönüşüm işlemlerinin güvenli ve hatasız bir şekilde yapılmasını sağlar.
class A { }
class B : A { }
class Program
{
static void Main()
{
A a = new A();
B b = new B();
A aAsB = new B();
Console.WriteLine(a is A); // True
Console.WriteLine(a is B); // False
Console.WriteLine(b is A); // True (B, A'dan türediği için)
Console.WriteLine(b is B); // True
Console.WriteLine(aAsB is A); // True
Console.WriteLine(aAsB is B); // True (aAsB'nin fiziksel türü B'dir)
Console.WriteLine(a is string); // False (A, string ile kalıtımsal ilişkiye sahip değil)
}
}
is
Operatörünün Özellikleri
Tür Kontrolü:
is
operatörü, bir nesnenin belirli bir türe ait olup olmadığını kontrol etmek için kullanılır.Bu, özellikle bir nesnenin belirli bir türe ait olup olmadığını bilmeniz gerektiğinde yararlıdır.
Güvenli Tür Dönüşümü:
is
operatörü ile bir tür kontrolü yaparak, daha sonra güvenli bir şekilde tür dönüşümü gerçekleştirebilirsiniz.
A a = new B();
if (a is B b)
{
b.MethodB(); // Güvenli tür dönüşümü ile MethodB'yi çağırabilirsiniz
}
else
{
Console.WriteLine("Dönüşüm başarısız.");
}
Bu kullanım, hem tür kontrolü hem de tür dönüşümünü aynı anda gerçekleştiren bir yapıdır ve is
operatörünün sağladığı güvenlikten yararlanır.
haliyle çok biçimlilik uygulanmış bir nesnenin ihtiyaç doğrultusunda ( uygun olan) farklı bir türe dönüştürülmesi için işi garantiye alabilmek adına önce is kontrolu ardından Cast yada as operasyonu sağlanması kafidir.
Yorum Yap