Bir alt sınıfta uyguladığımızda neden anahtar sözcüklerin soyut yöntemlerin önünde geçersiz kılınması gerekiyor?


9

Soyut bir sınıftan miras alan bir sınıf oluşturduğumuzda ve miras alınan soyut sınıfı uyguladığımızda neden override anahtar sözcüğünü kullanmak zorundayız?

public abstract class Person
{
    public Person()
    {

    }

    protected virtual void Greet()
    {
        // code
    }

    protected abstract void SayHello();
}

public class Employee : Person
{
    protected override void SayHello() // Why is override keyword necessary here?
    {
        throw new NotImplementedException();
    }

    protected override void Greet()
    {
        base.Greet();
    }
}

Yöntem üst sınıfında özet olarak bildirildiğinden, üst sınıfta herhangi bir uygulaması yoktur, bu yüzden burada anahtar kelime geçersiz kılma neden gereklidir?


2
Soyut bir yöntem, şartnamelere göre
Pavel Anikhouski


"Devralma değiştirici, devralınan bir yöntemin, özelliğin, dizinleyicinin veya etkinliğin soyut veya sanal uygulamasını genişletmek veya değiştirmek için gereklidir." docs.microsoft.com/tr-tr/dotnet/csharp/language-reference/…
gunr2171

2
Çünkü oraya koymazsanız, yöntemi geçersiz kılmak yerine "gizlersiniz". Bu yüzden " newİstediğiniz buysa anahtar kelimeyi kullanın ... " uyarısı da "hiçbir yöntem temel sınıf yöntemini geçersiz kılmaz" hatası alırsınız.
Ron Beyer

Sanal bir yöntemle @RonBeyer evet, ancak soyutla derlemez.
Johnathan Barclay

Yanıtlar:


14

Soyut bir sınıftan miras alan bir sınıf oluşturduğumuzda ve miras alınan soyut sınıfı uyguladığımızda neden override anahtar sözcüğünü kullanmak zorundayız?

"Neden?" bunun gibi sorulara cevap vermek zor olabilir, çünkü belirsizdirler. Sorunuzun " overrideanahtar kelimenin gerekli olduğu konum için tartışmak üzere dil tasarımı sırasında hangi argümanlar yapılabileceğini" varsayacağım. ?"

Geri adım atarak başlayalım. Java gibi bazı dillerde yöntemler varsayılan olarak sanaldır ve otomatik olarak geçersiz kılınır. C # tasarımcıları bunun farkındaydı ve Java'daki küçük bir kusur olarak kabul etti. C #, bazılarının söylediği gibi "aptal bölümleri çıkarılmış Java" değildir , ancak C # tasarımcıları C, C ++ ve Java'nın sorunlu tasarım noktalarından öğrenmek ve C #'da çoğaltmak istemiyorlardı.

C # tasarımcıları, geçersiz kılmanın olası bir hata kaynağı olduğunu düşündüler; sonuçta , mevcut, test edilmiş kodun davranışını değiştirmenin bir yoludur ve bu tehlikelidir. Geçersiz kılma, rasgele veya kazayla yapılması gereken bir şey değildir; bu konuda çok düşünen biri tarafından tasarlanmalıdır . Bu yüzden yöntemler varsayılan olarak sanal değildir ve neden bir yöntemi geçersiz kıldığınızı söylemeniz gerekir.

Temel akıl yürütme budur. Şimdi daha gelişmiş bir akıl yürütmeye başlayabiliriz.

StriplingWarrior'un cevabı, daha gelişmiş bir argüman yaratmada iyi bir ilk kesinti veriyor. Türetilmiş sınıfın yazarı temel sınıf hakkında bilgi sahibi olmayabilir, yeni bir yöntem yapmaya niyetli olabilir ve kullanıcının yanlışlıkla geçersiz kılmasına izin vermemeliyiz .

Bu nokta mantıklı olsa da, aşağıdakiler gibi bir dizi karşı-değişken vardır:

  • Türetilmiş bir sınıfın yazarı , temel sınıfla ilgili her şeyi bilmekle yükümlüdür ! Bu kodu yeniden kullanıyorlar ve yeniden kullanmadan önce bu kodu iyice anlamak için gereken özeni göstermelidirler.
  • Özel senaryoda sanal yöntem soyuttur; o bir hata olurdu değil geçersiz ve nedenle yazar kazara bir uygulama yaratmak olacağını olası değildir.

O zaman bu noktada daha da ileri düzeyde bir tartışma yapalım. Hangi koşullarda türetilmiş bir sınıfın yazarı temel sınıfın ne yaptığını bilmediği için mazur görülebilir? Bu senaryoyu düşünün:

  • Temel sınıf yazarı soyut bir temel sınıf B yapar.
  • Türetilmiş sınıf yazarı, farklı bir ekipte, yöntem M ile türetilmiş bir D sınıfı yapar.
  • Temel sınıf yazarı, temel sınıf B'yi genişleten takımların her zaman bir yöntem M sağlaması gerektiğini fark eder, bu nedenle temel sınıf yazarı soyut yöntem M ekler.
  • D sınıfı yeniden derlendiğinde ne olur?

Olmak istediğimiz şey, D'nin yazarı, ilgili bir şeyin değiştiği konusunda bilgilendirildi . Değişen ilgili şey, M'nin artık bir gereksinim olduğu ve bunların uygulanmasının aşırı yüklenmesi gerektiğidir. Temel sınıftan çağrılabileceğini bildiğimizde DM'nin davranışını değiştirmesi gerekebilir. Yapılacak doğru şey sessizce "oh, DM vardır ve BM'yi genişletir" demek değildir . Derleyicinin yapacağı doğru şey başarısızdır ve "hey, D'nin yazarı, artık geçerli olmayan bu varsayımına göz atın ve gerekirse kodunuzu düzeltin" deyin.

Örnekte, varsayalım overrideoldu isteğe üzerinde SayHellobir soyut yöntemi geçersiz çünkü. İki olasılık vardır: (1) kodun yazarı soyut bir yöntemi geçersiz kılmak niyetindedir veya (2) bir başkası temel sınıfı değiştirdiğinden ve kod şimdi ince bir şekilde yanlış olduğundan , geçersiz kılma yöntemi yanlışlıkla geçersiz kılınır . İsteğe bağlı ise bu olasılıkları birbirinden overrideayıramayız .

Ama eğer overrideedilir gerekli o zaman ayrı üç senaryo söyleyebilir. Olası bir hata kodu varsa o zaman overridealmaktadır eksik . Kasıtlı geçersiz ise o zaman overrideolduğu mevcut . Ve bilerek eğer değil o zaman ağır basan newbir mevcut . C # 'ın tasarımı bu ince ayrımları yapmamızı sağlar.

Unutmayın raporlama geliştiricinin zihin okuma gerektiren derleyici hatası ; derleyici , yazarın aklındaki doğru kodu yanlış koddan çıkarmalı ve doğru yönde gösteren bir hata vermelidir. Geliştiricinin ne düşündüğüne dair kodda ne kadar fazla ipucu bırakabileceğimize dair, derleyicinin hataları raporlamada yapabileceği daha iyi bir iş ve dolayısıyla hatalarınızı daha hızlı bulabilir ve düzeltebilirsiniz.

Ancak daha genel olarak, C #, kodun değiştiği bir dünya için tasarlanmıştır . "Tek" gibi görünen birçok C # özelliği aslında oradadır, çünkü temel sınıf değiştiği için geçerli olan bir varsayım geçersiz olduğunda geliştiriciyi bilgilendirirler . Bu hata sınıfına "gevrek temel sınıf başarısızlıkları" denir ve C #, bu başarısızlık sınıfı için bir dizi ilginç azaltıcı etkiye sahiptir.


4
Ayrıntılandırdığınız için teşekkürler. Cevaplarınızı her zaman takdir ediyorum, çünkü hem "içeriden" birinden yetkili bir sese sahip olmak iyi ve her şeyi basit ve tamamen açıklamak için harika bir iş yaptığınız için.
StriplingWarrior

Derinlemesine açıklama için teşekkür ederim! gerçekten takdir ediyorum.
psj01

Harika noktalar! Bahsettiğiniz diğer azaltıcı etkenleri listeleyebilir misiniz?
aksh1618

3

Üst sınıftaki başka bir yöntemi geçersiz kılmaya çalıştığınızı veya sınıf hiyerarşisinin bu düzeyine özgü yeni bir uygulama oluşturup oluşturmayacağınızı belirtmektir. Bir programcının, sınıflarında oluşturdukları imzayla tamamen aynı imzası olan bir ebeveyn sınıfında bir yöntemin varlığından haberdar olmayabileceği düşünülebilir ve bu da bazı kötü sürprizlere yol açabilir.

Soyut bir yöntemin soyut olmayan bir çocuk sınıfında geçersiz kılınması gerektiği doğru olsa da , C # zanaatkarları muhtemelen ne yapmaya çalıştığınız konusunda açık olmanın daha iyi olduğunu hissettiler.


Bu, dil tasarım ekibinin mantığını anlamak için iyi bir başlangıçtır; Ekibin burada ifade ettiğiniz fikirle nasıl başladığını gösteren bir yanıt ekledim, ancak bunu bir adım daha ileri götürüyor.
Eric Lippert

1

Çünkü abstractyöntem C # dili başına hiçbir uygulanması ile sanal yöntem olup şartname soyut yöntem örtük bir sanal yöntemdir, araçlar. Ve overridegördüğünüz gibi, soyut ya da sanal uygulama uzatmak veya değiştirmek için kullanılır burada

Biraz yeniden ifade etmek için - bir tür geç bağlama uygulamak için sanal yöntemler kullanırsınız, oysa soyut yöntemler, türün alt sınıflarını, yöntemin açıkça geçersiz kılınmasına zorlar. Yani metodu olduğunda, nokta virtual, bu olabilir bir olunca, geçersiz kılınan olmak abstract- bu olmalı overriden olmak


1
Bence soru ne yaptığı ile değil, neden açık olması gerektiğiyle ilgili.
Johnathan Barclay

0

@ StriplingWarrior yanıtına eklemek için, temel sınıftaki sanal bir yöntemi geçersiz kılmakla tutarlı bir sözdizimine sahip olmanın da mümkün olduğunu düşünüyorum.

public abstract class MyBase
{
    public virtual void MyVirtualMethod() { }

    public virtual void MyOtherVirtualMethod() { }

    public abstract void MyAbtractMethod();
}

public class MyDerived : MyBase
{
    // When overriding a virtual method in MyBase, we use the override keyword.
    public override void MyVirtualMethod() { }

    // If we want to hide the virtual method in MyBase, we use the new keyword.
    public new void MyOtherVirtualMethod() { }

    // Because MyAbtractMethod is abstract in MyBase, we have to override it: 
    // we can't hide it with new.
    // For consistency with overriding a virtual method, we also use the override keyword.
    public override void MyAbtractMethod() { }
}

Bu nedenle C # , soyut yöntemleri geçersiz kılmak için geçersiz kılma anahtar kelimesine ihtiyacınız olmayacak şekilde tasarlanmış olabilir , ancak tasarımcıların sanal bir yöntemi geçersiz kılma ile tutarlı olmayacağından kafa karıştırıcı olacağına karar verdim.


Re: "tasarımcılar, sanal bir yöntemi geçersiz kılmak ile tutarlı olmayacağı için kafa karıştırıcı olacağına karar verdi" - evet, ama daha fazlası. Sanal yöntem M ile bir temel sınıf B'ye ve bir geçersiz kılma ile türetilmiş bir D sınıfına sahip olduğunuzu varsayalım. Şimdi B'nin yazarının M soyut yapmaya karar verdiğini varsayalım. Bu çok büyük bir değişiklik, ama belki de böyle yapıyorlar. Soru: D'nin yazarından, kaldırılması istenmeli overridemi? Çoğu insanın D yazarını gereksiz bir kod değişikliği yapmaya zorlamanın saçma olduğunu kabul edeceğini düşünüyorum; onların sınıfı iyi!
Eric Lippert
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.