Menu

C# Kalıtım (Inheritance): base, virtual, override, sealed ve Polimorfizm

C#'ta sınıf kalıtımı nasıl çalışır: iki nokta ile türetmek, neyin kalıtıldığı ve neyin kalıtılmadığı, base ile temel sınıfı çağırmak, virtual ve override, new ile gizlemek (ve herkesi şaşırtan çıktı), sealed, polimorfizm ve bir hiyerarşide yukarı ve aşağı cast.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Kalıtım, bir sınıfın başka bir sınıfın üzerine inşa edilmesini sağlar. Türetilmiş sınıf temel sınıfın üyelerini alır, kendi üyelerini ekler ve temel sınıfın değiştirmesine izin verdiği davranışı değiştirebilir. virtual metotlarla birleştiğinde size polimorfizm verir: temel sınıfa karşı yazılmış kod, hangi türetilmiş sınıfa sahip olduğunu bilmeden doğru türetilmiş davranışı çalıştırır.

Bir sınıf türetmek

Temel sınıfı iki noktadan sonra yazın. Türetilmiş sınıf temel sınıfın sahip olduğu her şeye ve bildirdiklerine sahiptir:

Çıktı:

TX-19: 20 km, 2 fares
True

Türetilmiş bir sınıfın aldıkları ve almadıkları:

  • Kalıtılanlar: alanlar, property'ler, metotlar, event'ler ve iç içe tipler. Her şey fiziksel olarak nesnenin içindedir.
  • Erişilebilenler: yalnızca temelin izin verdikleri: public, protected ve internal üyeler. Vehicle'ın private bir üyesi her Taxi'nin içinde vardır ama Taxi'nin kodu onun adını anamaz. Km'nin private set'e sahip olmasının nedeni budur: Taxi onu okuyabilir ve yalnızca Drive üzerinden değiştirir.
  • Kalıtılmayanlar: constructor'lar. Taxi kendi constructor'ını bildirmeli ve : base(plate) ile Vehicle'ınkilerden birine zincirlemelidir. Constructor'lar sayfası ikisinin çalışma sırasını gösterir.

Her sınıf sonuçta object'ten türer; her nesnenin ToString(), Equals() ve GetHashCode()'u olmasının nedeni budur.

virtual ve override

Bir temel sınıf, "türetilmiş sınıflar kendi sürümlerini sağlayabilir" demek için bir metodu virtual işaretler. Türetilmiş bir sınıf onu override ile değiştirir. Override'ın içinde base.Method() temel sınıfın sürümünü çağırır.

Çıktı:

[email] to lea@example.com: Your order 1042 has shipped today
  (unsubscribe link appended)
[sms] to +351 912 000 111: Your order 1042 has...
[generic] to ops-team: Your order 1042 has shipped today

Döngü değişkeninin tipi Notification'dır, ama yine de her nesne kendi şekilde oluşturulur. Polimorfizm budur: n.Render(...) çağrısı çalışma zamanında nesnenin gerçek tipinden çözülür. Ayrıca Email'deki base.Render'ın kendisi de virtual olan Channel'ı kullandığına dikkat edin; bu yüzden temel metot generic değil email yazdırır. Temel sınıfın içindeki bir virtual çağrı yine override'a ulaşır.

Channel'ın gösterdiği gibi property'ler de virtual olabilir. Alanlar olamaz.

Derleyici sizi iki anahtar kelimeden de sorumlu tutar. virtual olmayan bir metoda override yazmak CS0506 hatasıdır ("cannot override inherited member ... because it is not marked virtual, abstract, or override"). Temel metot virtual iken override'ı atlamak yalnızca bir uyarıdır ve bir sonraki bölümün gösterdiği gibi anlamı tamamen değiştirir.

new ile override: override etmek yerine gizlemek

Türetilmiş bir sınıf, override yazmadan bir temel metotla aynı imzaya sahip bir metot bildirirse temel metodu gizler. Derleyici uyarır (virtual bir temel metot için CS0114, aksi halde CS0108) ve uyarıyı susturan ama gizleme davranışını koruyan new anahtar kelimesini önerir:

Çıktı:

Sales report
Report
Draft report

b ve c aynı türden nesnelerdir, ama farklı başlıklar yazdırırlar. new ile seçilen metot, derleme zamanında belirlenen değişkenin tipine bağlıdır. Raporları Report olarak ele alan kod (bir liste, bir metot parametresi, bir framework callback'i) DraftReport.Title'ı asla görmez. Bu neredeyse hiçbir zaman istediğiniz şey değildir. Polimorfizm için override kullanın; new esas olarak, kontrol etmediğiniz bir temel sınıfın, sizde zaten bulunan bir üyeyle adı çakışan bir üye eklemesi durumu için vardır.

sealed

Bir sınıftaki sealed ondan türetmeyi yasaklar:

sealed class Invoice { }
class CorrectedInvoice : Invoice { }
// error CS0509: 'CorrectedInvoice': cannot derive from sealed type 'Invoice'

string ve birçok framework tipi sealed'dır. Bir override üzerinde sealed zinciri o seviyede durdurur:

class Shape { public virtual string Name() => "shape"; }
class Square : Shape { public sealed override string Name() => "square"; }
class Tile : Square { public override string Name() => "tile"; }
// error CS0239: 'Tile.Name()': cannot override inherited member 'Square.Name()' because it is sealed

Bir sınıfı kalıtım için tasarlamak emek ister: neyin virtual olduğuna, türetilmiş sınıfların neye güvenebileceğine, işlerin hangi sırayla olduğuna karar vermek. Böyle tasarlanmamış bir sınıf sealed olarak daha güvenlidir ve sealed yapmak sonradan kimseyi bozmadan geri alınabilir; oysa başkaları sizden türettiğinde unsealed yapmak geri alınamaz. Sealed sınıfların üyelerine yapılan çağrılar biraz daha hızlı da olabilir, çünkü çalışma zamanı hiçbir override olmadığını bilir.

Tek temel sınıf, çok sayıda interface

Bir C# sınıfının tam olarak bir temel sınıfı vardır. class Admin : User, Employee CS1721 hatasıdır ("cannot have multiple base classes"). Ancak bir sınıf temel sınıfının yanında istediği sayıda interface uygulayabilir:

class Admin : User, IAuditable, IComparable<Admin>
{
    // base class first, then interfaces, in any order
}

"Bir türüdür ve uygulamayı onunla paylaşır" için bir temel sınıf, "yapabilir" için interface'ler kullanın. Temel sınıf yalnızca türetilmiş sınıfları bazı metotları doldurmaya zorlamak için varsa, doğru araç bir abstract sınıftır.

Hiyerarşide yukarı ve aşağı cast

Türetilmiş bir nesne her zaman temel tipinin beklendiği yerde kullanılabilir. Bu yukarı cast örtüktür ve başarısız olamaz. Diğer yöne, aşağı cast'e gitmek açık bir cast gerektirir ve nesne o tipte değilse çalışma zamanında başarısız olur:

Çıktı:

Rex fetches the ball
InvalidCastException: Tom is not a Dog
True
Rex fetches the ball

Cast olmadan Dog d = pet; yazmak bir derleme hatasıdır (CS0266: "An explicit conversion exists (are you missing a cast?)"), çünkü derleyici yalnızca pet'in bir tür Animal olduğunu bilir. Nesne başka bir tipte olabiliyorsa değişkenli is'i, düz bir cast'i ise yalnızca başka bir şeyin hata olacağı durumda tercih edin. Sık aşağı cast bir tasarım kokusudur: genellikle davranışın temel sınıftaki virtual bir metoda ait olduğu anlamına gelir. Pattern matching sayfası is biçimlerini eksiksiz ele alır.

Kalıtım ve koleksiyonlar

Polimorfizm eleman eleman çalışır, ama türetilmiş bir tipten oluşan generic koleksiyonlar temel tipin koleksiyonları değildir: List<Animal> animals = new List<Dog>(); derlenmez, çünkü o zaman liste bir Cat kabul ederdi. Salt okunur görünümler kovaryanttır, bu yüzden IEnumerable<Animal> animals = new List<Dog>(); sorun değildir. Generics sayfası nedenini açıklar.

Yaygın hatalar

  • override'ı unutmak. Metot bir uyarıyla derlenir ve override etmek yerine sessizce gizler. CS0114'ü hata gibi ele alın.
  • Her şeyi virtual yapmak. Her virtual üye, türetilmiş sınıflara ne zaman çağrılacağı ve neyi varsayabileceği hakkında bir sözdür. Yalnızca niyet ettiğiniz genişletme noktalarını işaretleyin.
  • Derin hiyerarşiler. Üç dört seviyelik kalıtım, bir metodun hangi sürümünün çalıştığını bilmeyi zorlaştırır. Kompozisyon, yani bir sınıfın başka bir nesneyi tutup onu çağırması, çoğu zaman yeni bir seviyeden daha basittir.
  • Bir constructor'dan virtual metotlar çağırmak. Override türetilmiş constructor'ın gövdesinden önce çalışır, bu yüzden o gövdenin atadığı her alan hâlâ varsayılan değerini tutar.
  • Kalıtımı yalnızca kodu yeniden kullanmak için kullanmak. Bir Stack List'ten kalıtım alırsa çağıranlar stack'inizin ortasına Insert yapabilir. Bunun yerine private bir alanda bir List tutun.

Sıkça Sorulan Sorular

C#'ta kalıtım nasıl çalışır?

Bir sınıf iki noktadan sonra bir temel sınıf adı yazar: class Dog : Animal. Türetilmiş sınıf temel sınıfın tüm üyelerini (alanlar, property'ler, metotlar, event'ler) alır, kendi üyelerini ekleyebilir ve temelin virtual işaretlediklerini override edebilir. Constructor'lar kalıtılmaz ve private üyeler, nesnede bulunsalar da, türetilmiş sınıftan erişilebilir değildir.

C#'ta virtual ile override arasındaki fark nedir?

virtual temel sınıf metoduna yazılır ve "türetilmiş sınıflar bunu değiştirebilir" der. override türetilmiş sınıf metoduna yazılır ve değiştirmeyi yapar. İkisi de gereklidir: virtual, abstract ya da zaten override olmayan bir metodu override etmek CS0506 hatasıdır. Virtual bir metot çağrıldığında çalışma zamanı, değişkenin tipi için değil nesnenin gerçek tipi için olan sürümü çalıştırır.

C#'ta new ile override arasındaki fark nedir?

override temel metodu, nesneyi bir temel sınıf değişkeni üzerinden tutan kod dahil her çağıran için değiştirir. new yalnızca onu gizler: nesneyi türetilmiş tip olarak gören kod yeni metodu çağırır, onu temel tip olarak gören kod ise hâlâ temel metodu çağırır. Yani Base b = new Derived(); b.M(); override ile Derived.M'yi, new ile Base.M'yi çalıştırır.

C# çoklu kalıtımı destekler mi?

Sınıflar için hayır: bir sınıfın tam olarak bir temel sınıfı vardır ve iki tane listelemek CS1721 hatasıdır. Bir sınıf istediği sayıda interface uygulayabilir; C# "bu tip birkaç şey yapabilir" fikrini böyle modeller. C# 8'den beri interface'ler varsayılan metot uygulamaları da taşıyabilir.

C#'ta sealed ne anlama gelir?

sealed bir sınıf temel sınıf olarak kullanılamaz; ondan türetmek CS0509 hatasıdır. Örneğin string sealed'dır. Bir metotta sealed override, hiyerarşinin daha aşağısındaki sınıfların onu yeniden override etmesini durdurur (CS0239). Kalıtım için tasarlanmamış sınıfları sealed yapmak makul bir varsayılandır.

C#'ta temel sınıf metodu nasıl çağrılır?

Türetilmiş sınıfın içinde, genellikle override'ın içinde base.MethodName(...) kullanın: public override string Describe() => base.Describe() + " with GPS";. Constructor'lar için hangi temel constructor'ın çalışacağını seçmek üzere parametre listesinden sonra : base(...) kullanın.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA