C # neden böyle yaptığını sorduğundan beri, C # yaratıcılarına sorman en iyisi. Onlar bir in (Java gibi) varsayılan olarak sanal ile gitmemesi için seçtim Anders Hejlsberg, C # için kurşun mimar, cevap mülakat , ilgili parçacıkları aşağıda belirtilmiştir.
Bir yöntemi sanal olmayan olarak işaretlemek için Java'nın varsayılan olarak son anahtar kelimeyle sanal olduğunu unutmayın. Hala öğrenmesi gereken iki kavram var, ancak çoğu kişi son anahtar kelimeyi bilmiyor veya proaktif olarak kullanmıyor. C #, bilinçli olarak bu kararları vermek için sanal ve yeni / geçersiz kılmayı kullanmaya zorlar.
Birkaç sebep var. Biri performans . İnsanlar Java’da kod yazarken, yöntemlerini kesin olarak işaretlemeyi unuttuklarını görebiliyoruz. Bu nedenle, bu yöntemler sanaldır. Sanal oldukları için de performans göstermiyorlar. Sanal bir yöntem olmakla ilgili sadece performans ek yükü var. Bu bir sorun.
Daha önemli bir konu versiyonlama . Sanal yöntemler hakkında iki düşünce okulu vardır. Düşüncenin akademik okulu, "Her şey sanal olmalı, çünkü bir gün geçersiz kılmak isteyebilirim" diyor. Gerçek dünyada çalışan gerçek uygulamalar oluşturmaktan gelen pragmatik düşünce okulu, "Sanallaştırdıklarımıza gerçekten çok dikkat etmeliyiz" diyor.
Bir platformda sanal bir şey yaptığımızda, gelecekte nasıl geliştiği konusunda çok fazla söz veriyoruz. Sanal olmayan bir yöntem için, bu yöntemi çağırdığınızda, x ve y'nin olacağına söz veriyoruz. Bir API'de sanal bir yöntem yayınladığımızda, yalnızca bu yöntemi çağırdığınızda x ve y'nin olacağına söz vermeyiz. Ayrıca, bu yöntemi geçersiz kıldığınızda, diğerlerine nazaran bu belirli sıraya göre arayacağımıza ve devletin bu durumda ve o değişmez olacağına söz veriyoruz.
Bir API'de sanal olarak her söylediğinizde, geri arama kancası yaratıyorsunuz. Bir işletim sistemi veya API çerçeve tasarımcısı olarak, bu konuda gerçekten dikkatli olmalısınız. Kullanıcıların bir API'deki herhangi bir noktada geçersiz kılmalarını ve takmalarını istemezsiniz, çünkü bu sözleri vermeniz gerekmez. Ve insanlar sanal bir şey yaptıklarında verdikleri sözleri tam olarak anlayamayabilirler.
Mülakat, geliştiricilerin sınıf mirası tasarımı hakkında nasıl düşündüğü ve bunun kararlarına nasıl yol açtığı hakkında daha fazla tartışmaya sahiptir.
Şimdi şu soruya:
Neden dünyada DerivedClass'ımda BaseClass ile aynı isimde ve aynı imzayla bir yöntem ekleyeceğimi ve yeni bir davranış tanımlayacağımı anlayamıyorum ama çalışma zamanı polimorfizminde BaseClass yöntemi çağrılacak! (geçersiz kılmıyor ama mantıklı olmalı).
Bu, türetilmiş bir sınıfın, temel sınıfın sözleşmesine uymadığını, aynı adı taşıyan bir yöntemi olduğunu beyan etmek istediğinde olur. ( C # new
ve override
arasındaki farkı bilmeyenler için bu MSDN sayfasına bakın ).
Çok pratik bir senaryo şudur:
- Sınıfı olan bir API oluşturdunuz
Vehicle
.
- API'nizi kullanmaya başladım ve türetildim
Vehicle
.
- Sizin
Vehicle
sınıf herhangi bir yöntemi yoktu PerformEngineCheck()
.
- Benim içinde
Car
sınıfa, bir yöntemi ekleyin PerformEngineCheck()
.
- API'nizin yeni bir versiyonunu yayınladınız ve a eklediniz
PerformEngineCheck()
.
- İstemcilerim API'ma bağlı olduğundan ve yöntemimi değiştireceği için yöntemimi yeniden adlandıramıyorum.
Yeni API’nize karşı yeniden derlediğimde, C # bu konuda beni uyarıyor;
Baz PerformEngineCheck()
değilse virtual
:
app2.cs(15,17): warning CS0108: 'Car.PerformEngineCheck()' hides inherited member 'Vehicle.PerformEngineCheck()'.
Use the new keyword if hiding was intended.
Ve baz eğer PerformEngineCheck()
idi virtual
:
app2.cs(15,17): warning CS0114: 'Car.PerformEngineCheck()' hides inherited member 'Vehicle.PerformEngineCheck()'.
To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
Şimdi, sınıfımın gerçekten temel sınıfın sözleşmesini uzatıp uzatmayacağı ya da farklı bir sözleşme olup olmadığı ancak aynı isimde olacağı konusunda açıkça bir karar vermeliyim.
- Bunu yaparak
new
, temel yöntemin işlevselliği türetilmiş yöntemden farklıysa, müşterilerimi bozmam. Herhangi başvurulan kod Vehicle
görmeyeceklerinden Car.PerformEngineCheck()
bir başvuru vardı aradım ama kod için Car
ben de teklif ettiğini aynı işlevselliği görmeye devam edecektir PerformEngineCheck()
.
Benzer bir örnek, temel sınıftaki başka bir yöntemin PerformEngineCheck()
(özellikle de yeni sürümde) çağrılabileceği durumlarda PerformEngineCheck()
, türetilmiş sınıfın aranmasını nasıl önler ? Java'da bu karar temel sınıfa aittir, ancak türetilen sınıf hakkında hiçbir şey bilmiyor. C # 'da, bu karar hem temel sınıfa ( virtual
anahtar kelime aracılığıyla ) hem de türetilmiş sınıfa ( new
ve override
anahtar kelimeler aracılığıyla ) dayanır .
Elbette, derleyicinin attığı hatalar, programcıların beklenmedik bir şekilde hata yapmamaları için de yararlı bir araç sağlar (yani, bunu gerçekleştirmeden geçersiz kılar veya yeni işlevler sağlar).
Anders'in dediği gibi, gerçek dünya bizi sıfırdan başlasak asla girmek istemediğimiz sorunlara zorlar.
EDIT: new
Arayüz uyumluluğunu sağlamak için nerede kullanılması gerektiğine dair bir örnek eklendi .
EDIT: Yorumlardan geçerken, diğer örnek senaryolarda (Brian'ın bahsettiği) Eric Lippert'in (sonra C # tasarım komitesinin üyelerinden biri) yazdığı bir yazıyla da karşılaştım.
BÖLÜM 2: Güncellenmiş soruya göre
Fakat eğer C # durumunda eğer SpaceShip Araç sınıfını geçersiz kılmazsa 'hızlanır ve yeni kullanırsanız, kodumun mantığı bozulur. Bu bir dezavantaj değil mi?
Kimin SpaceShip
gerçekten geçersiz kılmaya Vehicle.accelerate()
ya da farklı olup olmadığına kim karar veriyor ? SpaceShip
Geliştirici olmalı . Eğer SpaceShip
geliştirici, temel sınıfın sözleşmesini yerine getirmediğine karar verirse, o zaman aramanız Vehicle.accelerate()
gitmeli SpaceShip.accelerate()
mi, yoksa gitmeli mi? O zaman bunu işaretleyecekler new
. Bununla birlikte, sözleşmeyi gerçekten tutturmaya karar verirse, o zaman aslında onu işaretleyecektir override
. Her iki durumda da, sözleşmenize bağlı olarak doğru yöntemi arayarak kodunuz doğru şekilde davranır . Kodunuz SpaceShip.accelerate()
gerçekten geçersiz kılmaya Vehicle.accelerate()
ya da bir isim çarpışması olup olmadığına nasıl karar verebilir ? (Yukarıdaki örneğime bakın).
Ancak, örtülü miras durumunda, bile SpaceShip.accelerate()
tutmadı sözleşme ait Vehicle.accelerate()
, yöntem çağrısı hala gider SpaceShip.accelerate()
.