Türetilmiş sınıfımdaki bir yöntemi neden çağırmak temel sınıf yöntemini çağırıyor?


146

Bu kodu düşünün:

class Program
{
    static void Main(string[] args)
    {
        Person person = new Teacher();
        person.ShowInfo();
        Console.ReadLine();
    }
}

public class Person
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}
public class Teacher : Person
{
    public new void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

Bu kodu çalıştırdığımda, aşağıdakiler çıktı:

Ben Kişiyim

Ancak, bunun bir örneği olduğunu görebilirsiniz Teacher, değil Person. Kod bunu neden yapıyor?


3
Bir Java kişisinden soru: Console.ReadLine (); bu örnek için gerekli mi?
Rich

2
@Shahrooz Sorunuza cevap veremiyorum - C # bilmiyorum. Çok önemsiz bir C # sorusu soruyordum, bu kişi ve öğretmen sınıflarında WriteLine'ı çağırabilmek için ana yöntemde ReadLine çağrısının gerekli olup olmadığıdır.
Rich

6
Evet, .Net, Main () çıkış yaptığında konsol penceresini otomatik olarak kapatır. bunu aşmak için, konsolun açık kalması için ek giriş beklemek üzere Console.Read () veya Console.Readline () kullanırız.
Kaptan Kenpachi

15
@ Zengin hayır, gerekli değil , ancak bu nedenle sık sık göreceksiniz: Visual Studio'dan bir konsol programı çalıştırırken, program sonlandırıldığında komut penceresi hemen kapanır, bu nedenle program çıktısını görmek istiyorsanız bunu söylemeniz gerekir beklemek.
AakashM

1
@AakashM Teşekkürler - Zamanımı, konsolun Eclipse penceresinin bir parçası olduğu ve bu yüzden kapanmadığı Eclipse'de geçiriyorum. Bu çok mantıklı.
Rich

Yanıtlar:


367

newVe virtual/ arasında bir fark var override.

Bir sınıfın somutlaştırıldığında, yöntemlerinin gerçek uygulamasına işaret eden bir işaretçiler tablosundan başka bir şey olmadığını hayal edebilirsiniz. Aşağıdaki görsel bunu oldukça iyi görselleştirmelidir:

Yöntem uygulamalarının gösterimi

Şimdi farklı yollar var, bir yöntem tanımlanabilir. Her biri kalıtımla kullanıldığında farklı davranır. Standart yol her zaman yukarıdaki resimde gösterildiği gibi çalışır. Bu davranışı değiştirmek isterseniz, yönteminize farklı anahtar sözcükler ekleyebilirsiniz.

1. Özet dersleri

İlki abstract. abstractyöntemler hiçbir yere işaret etmez:

Soyut sınıfların çizimi

Sınıfınız soyut üyeler içeriyorsa, aynı zamanda olarak işaretlenmesi gerekir abstract, aksi takdirde derleyici uygulamanızı derlemez. abstractSınıfların örneklerini oluşturamazsınız , ancak bunlardan devralabilir ve devralınan sınıflarınızın örneklerini oluşturabilir ve temel sınıf tanımını kullanarak bunlara erişebilirsiniz. Örneğinizde bu şöyle görünecektir:

public abstract class Person
{
    public abstract void ShowInfo();
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a teacher!");
    }
}

public class Student : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a student!");
    }
}

Çağrılırsa, davranışı ShowInfouygulamaya göre değişir:

Person person = new Teacher();
person.ShowInfo();    // Shows 'I am a teacher!'

person = new Student();
person.ShowInfo();    // Shows 'I am a student!'

Hem Students hem Teacherde Persons, ancak kendileri hakkında bilgi istemeleri istendiğinde farklı davranırlar. Ancak, onlardan bilgilerini istemelerini istemenin yolu aynıdır: PersonSınıf arayüzünü kullanmak .

Peki, miras aldığınızda perde arkasında ne olur Person? Uygulama ShowInfosırasında, işaretçi artık hiçbir yere işaret etmiyor, şimdi gerçek uygulamaya işaret ediyor! Bir Studentörnek oluştururken , Students'ye işaret eder ShowInfo:

Kalıtsal yöntemlerin gösterimi

2. Sanal yöntemler

İkinci yol, virtualyöntemleri kullanmaktır . Temel sınıfınızda isteğe bağlı bir varsayılan uygulama sağlamanız dışında davranış aynıdır . İle sınıfları virtualüyeleri instanciated olabilir, bununla birlikte kalıtsal sınıfları, farklı uygulamalar sağlayabilir. İşte kodunuzun çalışması için gerçekte nasıl görünmesi gerektiği:

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am a person!");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a teacher!");
    }
}

Temel fark, temel üyenin artık hiçbir yerePerson.ShowInfo işaret etmemesidir. Bu aynı zamanda, neden örnek oluşturabilmenizin nedenidir (ve bu nedenle artık olarak işaretlenmesine gerek yoktur ):Personabstract

Temel sınıf içindeki sanal üyenin çizimi

Şimdilik ilk resimden farklı görünmediğini fark etmelisiniz. Bunun nedeni, virtualyöntemin " standart yolu " bir uygulamaya işaret etmesidir . Kullanılması virtual, görebiliyorsun Personso, olabilir (değil gerekir için farklı bir uygulama sağlamak) ShowInfo. Yukarıda overrideyaptığım gibi farklı bir uygulama (kullanarak ) sağlarsanız Teacher, görüntü ile aynı görünecektir abstract. Aşağıdakiler için özel bir uygulama sağlamadığımızı düşünün Student:

public class Student : Person
{
}

Kod şu şekilde adlandırılacaktır:

Person person = new Teacher();
person.ShowInfo();    // Shows 'I am a teacher!'

person = new Student();
person.ShowInfo();    // Shows 'I am a person!'

Ve için resim Studentşöyle görünecektir:

Bir yöntemin varsayılan uygulamasının sanal anahtar sözcük kullanılarak gösterimi

3. "Gölgeleme" olarak da bilinen sihirli "yeni" anahtar kelime

newdaha çok bunun etrafında bir hack. Temel sınıf / arabirimdeki yöntemlerle aynı adlara sahip genelleştirilmiş sınıflarda yöntemler sağlayabilirsiniz. Her ikisi de kendi özel uygulamalarına işaret ediyor:

New-anahtar kelimesinin kullanıldığı "yolun" çizimi

Uygulama sağladığınız gibi görünüyor. Yönteme erişim şeklinize bağlı olarak davranış farklılık gösterir:

Teacher teacher = new Teacher();
Person person = (Person)teacher;

teacher.ShowInfo();    // Prints 'I am a teacher!'
person.ShowInfo();     // Prints 'I am a person!'

Bu davranış istenebilir, ancak sizin durumunuzda yanıltıcıdır.

Umarım bu, sizin için daha anlaşılır hale getirir!


9
Harika cevabınız için teşekkürler

6
Bu diyagramları oluşturmak için ne kullandınız?
BlueRaja - Danny Pflughoeft

2
Mükemmel ve çok kapsamlı cevap.
Nik Bougalis

8
tl; dr kullandığınız newyeni fonksiyon üst sınıfın işlevi ayrı kılan fonksiyonunun kalıtım sonları ve
mandal ucube

3
@Taymon: Aslında hayır ... Aramanın artık ters gittiğini açıklığa kavuşturmak istedim Person, değil Student;)
Carsten

45

C #'daki alt tip polimorfizmi, C ++ 'ya benzer, ancak Java'dan farklı olarak açık sanallık kullanır. Bu, yöntemleri açıkça geçersiz kılınabilir (yani virtual) olarak işaretlemeniz gerektiği anlamına gelir . C # 'da ayrıca overrideyazım hatalarını önlemek için geçersiz kılma yöntemlerini geçersiz kılma (yani ) olarak işaretlemeniz gerekir .

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

Sorunuza kod, kullanmak newyok ki, gölgeleme yerine basan. Gölgeleme, çalışma zamanı anlambiliminden ziyade yalnızca derleme zamanı anlambilimini, dolayısıyla istenmeyen çıktıyı etkiler.


4
OP'nin ne anlama geldiğini kim söyleyecek?
Cole Johnson

@ColeJohnson bir açıklama ekleyeceğim.

25

Üst sınıf başvurusuna koyduğunuz sınıf nesnesinin yöntemini çağırmak için yöntemi sanal yapmalı ve alt sınıftaki işlevi geçersiz kılmalısınız.

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}
public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

Sanal Yöntemler

Bir sanal yöntem çağrıldığında, nesnenin çalışma zamanı türü geçersiz kılan bir üye için kontrol edilir. En türetilmiş sınıftaki geçersiz kılan üye çağrılır ve türetilmiş sınıf üyeyi geçersiz kılmadıysa, orijinal üye olabilir. Varsayılan olarak yöntemler sanal değildir. Sanal olmayan bir yöntemi geçersiz kılamazsınız. Sanal değiştiriciyi statik, soyut, özel veya geçersiz kılma değiştiricileri olan MSDN ile kullanamazsınız .

Gölgeleme için Yeniyi Kullanma

Geçersiz kılmak yerine yeni anahtar kelime kullanıyorsunuz, yeni olan bu

  • Türetilmiş sınıftaki yöntemden önce new veya override anahtar sözcükleri gelmiyorsa, derleyici bir uyarı verir ve yöntem, yeni anahtar sözcük varmış gibi davranır.

  • Eğer elde edilen sınıfında yöntem yeni bir anahtar kelime ile önce, yöntem, temel sınıftaki bağımsız olarak tanımlanmıştır , bu MSDN makalesine çok iyi açıklamaktadır.

Erken bağlama VS Geç bağlama

Derleme zamanında normal yöntem (sanal değil) için derleme zamanında erken bağlanmamız var, bu şu anki durumdur, derleyicinin çağrıyı referans türü (temel sınıf) yöntemi olan temel sınıf yöntemine bağlayacağı, nesnenin temel başvurusunda tutulması sınıf ie türetilmiş sınıf nesnesi . Bunun nedeni ShowInfosanal bir yöntem olmamasıdır. Geç bağlama, sanal yöntem tablosu (vtable) kullanılarak (sanal / geçersiz kılınan yöntem) için çalışma zamanında gerçekleştirilir .

Normal bir işlev için, derleyici bellekteki sayısal konumunu hesaplayabilir. Daha sonra işlev çağrıldığında, işlevi bu adreste çağırmak için bir talimat üretebilir.

Herhangi bir sanal yöntemi olan bir nesne için derleyici bir v-tablosu oluşturacaktır. Bu esasen sanal yöntemlerin adreslerini içeren bir dizidir. Sanal bir yöntemi olan her nesne, derleyici tarafından oluşturulan ve v-tablosunun adresi olan gizli bir üye içerecektir. Bir sanal işlev çağrıldığında, derleyici v-tablosundaki uygun yöntemin konumunun ne olduğunu hesaplayacaktır. Daha sonra, nesnelerin v-tablosuna bakmak için kod üretecek ve bu konumda, Referans'ı sanal yöntemi çağıracaktır .


7

Achratt'ın cevabını geliştirmek istiyorum . Tamlık açısından fark, OP'nin newtüretilmiş sınıfın yöntemindeki anahtar sözcüğün temel sınıf yöntemini geçersiz kılmasını beklemesidir . Aslında yaptığı şey temel sınıf yöntemini gizlemektir .

C # 'da, belirtildiği gibi, geleneksel yöntemi geçersiz kılma açık olmalıdır; temel sınıf yöntemi olarak işaretlenmeli virtualve türetilmiş sınıf özellikle overridetemel sınıf yöntemi olmalıdır. Bu yapılırsa, nesnenin temel sınıfın veya türetilmiş sınıfın bir örneği olarak değerlendirilip değerlendirilmediği önemli değildir; türetilmiş yöntem bulunur ve çağrılır. Bu, C ++ 'da olduğu gibi benzer bir şekilde yapılır; "sanal" veya "geçersiz kılma" olarak işaretlenmiş bir yöntem, derlendiğinde, referans verilen nesnenin gerçek türünü belirleyerek ve nesne hiyerarşisini ağaç boyunca değişken türünden gerçek nesne türüne doğru aşağı doğru geçerek "geç" (çalışma zamanında) çözülür, değişken türü tarafından tanımlanan yöntemin en türetilmiş uygulamasını bulmak için.

Bu, "örtük geçersiz kılmalara" izin veren Java'dan farklıdır; örneğin yöntemler (statik olmayan), aynı imzaya sahip bir yöntemi (parametrelerin adı ve sayısı / türü) tanımlamak, alt sınıfın üst sınıfı geçersiz kılmasına neden olur.

Kontrol etmediğiniz sanal olmayan bir yöntemin işlevselliğini genişletmek veya geçersiz kılmak genellikle faydalı olduğundan, C # ayrıca newbağlamsal anahtar kelimeyi de içerir . newBunun yerine o ağır basan bir anahtar kelime "gizler" ana yöntemi. Devralınabilir herhangi bir yöntem sanal olsun ya da olmasın gizlenebilir; bu, geliştirici olarak, bir ebeveynden devralmak istediğiniz üyelerden, istemediklerinizin etrafında çalışmak zorunda kalmadan yararlanmanıza ve aynı "arayüzü" kodunuzun tüketicilerine sunmanıza izin verir.

Gizleme, nesnenizi gizleme yönteminin tanımlandığı kalıtım düzeyinde veya altında kullanan bir kişinin bakış açısından geçersiz kılmaya benzer şekilde çalışır. Sorunun örneğinden, bir Öğretmen yaratan ve bu referansı Öğretmen türünün bir değişkeninde depolayan bir kodlayıcı, Öğretmenden ShowInfo () uygulamasının davranışını görecek ve Öğretmenden bir kişiyi gizleyecektir. Ancak, bir Kişi kayıtları koleksiyonunda (sizin gibi) nesnenizle çalışan biri, ShowInfo () 'nun Kişi uygulamasının davranışını görecektir; Öğretmenin yöntemi, üstünü geçersiz kılmaz (ki bu da Person.ShowInfo () 'nun sanal olmasını gerektirir), Soyutlamanın Kişi düzeyinde çalışan kod, Teacher uygulamasını bulmaz ve onu kullanmaz.

Ek olarak, newanahtar kelime bunu açıkça yapmakla kalmaz , C # örtük yöntem gizlemeye de izin verir; basit biçimde, bir ana sınıf yöntemiyle aynı imza ile bir yöntemi oluşturan overrideya da new(bir derleyici uyarı ya da ReSharper veya CodeRush gibi bazı yeniden düzenleme yardımcıları bir şikayet üretecek olsa) olarak gizler. Bu, C # tasarımcılarının C ++ 'ın açık geçersiz kılmaları ile Java'nın örtülü olanları arasında ortaya çıkardığı uzlaşmadır ve zarif olmasına rağmen, eski dillerden herhangi birinde bir arka plandan geliyorsanız her zaman bekleyeceğiniz davranışı üretmez.

İşte yeni şeyler: İki anahtar kelimeyi uzun bir miras zincirinde birleştirdiğinizde bu karmaşık bir hal alır. Aşağıdakileri göz önünde bulundur:

class Foo { public virtual void DoFoo() { Console.WriteLine("Foo"); } }
class Bar:Foo { public override sealed void DoFoo() { Console.WriteLine("Bar"); } }
class Baz:Bar { public virtual void DoFoo() { Console.WriteLine("Baz"); } }
class Bai:Baz { public override void DoFoo() { Console.WriteLine("Bai"); } }
class Bat:Bai { public new void DoFoo() { Console.WriteLine("Bat"); } }
class Bak:Bat { }

Foo foo = new Foo();
Bar bar = new Bar();
Baz baz = new Baz();
Bai bai = new Bai();
Bat bat = new Bat();

foo.DoFoo();
bar.DoFoo();
baz.DoFoo();
bai.DoFoo();
bat.DoFoo();

Console.WriteLine("---");

Foo foo2 = bar;
Bar bar2 = baz;
Baz baz2 = bai;
Bai bai2 = bat;
Bat bat2 = new Bak();

foo2.DoFoo();
bar2.DoFoo();
baz2.DoFoo();
bai2.DoFoo();    

Console.WriteLine("---");

Foo foo3 = bak;
Bar bar3 = bak;
Baz baz3 = bak;
Bai bai3 = bak;
Bat bat3 = bak;

foo3.DoFoo();
bar3.DoFoo();
baz3.DoFoo();
bai3.DoFoo();    
bat3.DoFoo();

Çıktı:

Foo
Bar
Baz
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat

Beşli ilk setin tümü beklenmelidir; her düzey bir uygulamaya sahip olduğundan ve başlatılanla aynı türde bir nesne olarak başvurulduğundan, çalışma zamanı her çağrıyı değişken türü tarafından referans verilen miras düzeyine çözümler.

İkinci beşlik set, her bir örneğin yakın üst öğe türünün bir değişkenine atanmasının sonucudur. Şimdi, davranıştaki bazı farklılıklar sarsılıyor; foo2Bir gerçek olduğu Barbir şekilde dökme Foo, yine de gerçek bir nesne türü bar daha türetilen yöntem bulacaksınız. bar2bir Baz, ancak bunun aksine foo2, Baz Bar'ın uygulamasını açıkça geçersiz kılmadığı için (Engelleyemez; Engelleyemez sealed), "yukarıdan aşağıya" bakıldığında çalışma zamanı tarafından görülmez, bu nedenle bunun yerine Bar uygulaması çağrılır. Baz'ın newanahtar kelimeyi kullanmak zorunda olmadığına dikkat edin ; Anahtar kelimeyi atlarsanız bir derleyici uyarısı alırsınız, ancak C # 'daki örtük davranış ana yöntemi gizlemektir. baz2a, Bai, burada geçersiz kılar Bazsitesindekinewuygulama, dolayısıyla davranışı foo2'e benzer ; Bai'deki gerçek nesne türünün uygulaması çağrılır. bai2A her iki türden de geçersiz kılınan uygulaması olmayan ve basitçe ebeveynininkini kullanan a'dır.Bat, bu da ebeveyninin Baiyöntem uygulamasını gizler ve bar2Bai'nin uygulaması mühürlenmemiş olsa bile aynı şekilde davranır , bu nedenle teorik olarak Bat yöntemi gizlemek yerine geçersiz kılabilir. En sonunda,bat2Bak

Üçüncü beş set, tam yukarıdan aşağıya çözünürlük davranışını gösterir. Her şey aslında zincirdeki en türetilmiş sınıfın bir örneğine başvuruyor Bak, ancak değişken türünün her düzeyinde çözünürlük, kalıtım zincirinin bu düzeyinde başlayarak ve yöntemin en türetilmiş açık geçersiz kılma işlemine kadar derinlemesine inceleyerek gerçekleştirilir. olanlarda Bar, Baive Bat. Yöntem gizleme, böylece geçersiz kalıtım zincirini "kırar"; gizleme yönteminin kullanılabilmesi için yöntemi gizleyen kalıtım düzeyinde veya altında nesne ile çalışmanız gerekir. Aksi takdirde, gizli yöntem "ortaya çıkarılır" ve onun yerine kullanılır.


4

Lütfen C #: Polimorfizm'deki polimorfizm hakkında okuyun (C # Programlama Kılavuzu)

Bu oradan bir örnek:

Yeni anahtar sözcük kullanıldığında, değiştirilen temel sınıf üyeleri yerine yeni sınıf üyeleri çağrılır. Bu temel sınıf üyelerine gizli üyeler denir. Türetilmiş sınıfın bir örneği, temel sınıfın bir örneğine dönüştürülürse, gizli sınıf üyeleri yine de çağrılabilir. Örneğin:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.

3

Bunu yapmanız virtualve ardından bu işlevi geçersiz kılmanız gerekir Teacher. Türetilmiş bir sınıfa başvurmak için temel işaretçiyi miras alırken ve kullanırken, kullanarak onu geçersiz kılmanız gerekir virtual. sınıf yöntemini türetilmiş bir sınıf başvurusunda newsaklamak içindir, basebir basesınıf başvurusu değildir .


3

Bu konudaki bilgileri genişletmek için birkaç örnek daha eklemek istiyorum. Umarım bu da yardımcı olur:

Burada, türetilmiş bir tür bir temel türe atandığında olanların etrafındaki havayı temizleyen bir kod örneği verilmiştir. Bu bağlamda hangi yöntemler kullanılabilir ve geçersiz kılınan ve gizli yöntemler arasındaki fark.

namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.foo();        // A.foo()
            a.foo2();       // A.foo2()

            a = new B();    
            a.foo();        // B.foo()
            a.foo2();       // A.foo2()
            //a.novel() is not available here

            a = new C();
            a.foo();        // C.foo()
            a.foo2();       // A.foo2()

            B b1 = (B)a;    
            b1.foo();       // C.foo()
            b1.foo2();      // B.foo2()
            b1.novel();     // B.novel()

            Console.ReadLine();
        }
    }


    class A
    {
        public virtual void foo()
        {
            Console.WriteLine("A.foo()");
        }

        public void foo2()
        {
            Console.WriteLine("A.foo2()");
        }
    }

    class B : A
    {
        public override void foo()
        {
            // This is an override
            Console.WriteLine("B.foo()");
        }

        public new void foo2()      // Using the 'new' keyword doesn't make a difference
        {
            Console.WriteLine("B.foo2()");
        }

        public void novel()
        {
            Console.WriteLine("B.novel()");
        }
    }

    class C : B
    {
        public override void foo()
        {
            Console.WriteLine("C.foo()");
        }

        public new void foo2()
        {
            Console.WriteLine("C.foo2()");
        }
    }
}

Bir başka küçük anormallik, aşağıdaki kod satırı için:

A a = new B();    
a.foo(); 

VS derleyicisi (intellisense), a.foo () öğesini A.foo () olarak gösterecektir.

Bu nedenle, bir temel türe daha türetilmiş bir tür atandığında, 'temel tür' değişkeninin, türetilmiş bir türde geçersiz kılınan bir yönteme başvurulana kadar temel tür olarak hareket ettiği açıktır. Bu, üst ve alt türler arasında aynı ada sahip (ancak geçersiz kılınmayan) gizli yöntemler veya yöntemlerle biraz karşı sezgisel hale gelebilir.

Bu kod örneği, bu uyarıları tasvir etmeye yardımcı olacaktır!


2

C #, üst / alt sınıf geçersiz kılma davranışında java'dan farklıdır. Java'da varsayılan olarak tüm yöntemler sanaldır, bu nedenle istediğiniz davranış kutunun dışında desteklenir.

C # 'da bir yöntemi temel sınıfta sanal olarak işaretlemeniz gerekir, o zaman istediğinizi alırsınız.


2

Yeni anahtar kelime söyle o anki sınıfta yöntemi mısın tipi Öğretmen bir değişkende saklanır sınıf Öğretmen bir örneğini varsa sadece çalışır. Veya dökümleri kullanarak tetikleyebilirsiniz: ((Öğretmen) Kişi) .ShowInfo ()


1

Buradaki 'öğretmen' değişkeninin türü typeof(Person)ve bu tür Öğretmen sınıfı hakkında hiçbir şey bilmiyor ve türetilmiş türlerde herhangi bir yöntem aramaya çalışmıyor. Öğretmen sınıfının yöntemini çağırmak için değişkeninizi çevirmelisiniz:(person as Teacher).ShowInfo() .

Değer türüne göre belirli bir yöntemi çağırmak için, temel sınıfınızda 'sanal' anahtar sözcüğünü kullanmalı ve türetilmiş sınıflarda sanal yöntemleri geçersiz kılmalısınız. Bu yaklaşım, sanal yöntemler geçersiz kılınarak veya geçersiz kılınmadan türetilmiş sınıfların uygulanmasına izin verir. Temel sınıf metotları, sanallar içermeyen türler için çağrılacaktır.

public class Program
{
    private static void Main(string[] args)
    {
        Person teacher = new Teacher();
        teacher.ShowInfo();

        Person incognito = new IncognitoPerson ();
        incognito.ShowInfo();

        Console.ReadLine();
    }
}

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

public class IncognitoPerson : Person
{

}

1

Çok geç olabilir ... Ancak soru basit ve cevap aynı düzeyde karmaşık olmalıdır.

Kod değişkeninizde kişi Teacher.ShowInfo () hakkında hiçbir şey bilmiyor. Son yöntemi temel sınıf referansından çağırmanın bir yolu yoktur, çünkü bu sanal değildir.

Kalıtıma yararlı bir yaklaşım var - kod hiyerarşinizle ne söylemek istediğinizi hayal etmeye çalışın. Ayrıca, bir veya başka bir aracın kendisi hakkında ne söylediğini hayal etmeye çalışın. Örneğin, bir temel sınıfa sanal işlev eklerseniz şunları varsayarsınız: 1. varsayılan uygulamaya sahip olabilir; 2. Türetilmiş sınıfta yeniden uygulanabilir. Soyut işlev eklerseniz, bu yalnızca bir anlama gelir - alt sınıf bir uygulama oluşturmalıdır. Ancak düz işleviniz varsa - kimsenin onun uygulamasını değiştirmesini beklemiyorsunuz.


0

Derleyici bunu yapar çünkü bunun bir Teacher. Tek bildiği, bir Personya da ondan türetilmiş bir şey olduğudur. Yani tek yapabileceği Person.ShowInfo()yöntemi çağırmaktır .


0

Sadece kısa bir cevap vermek istedim -

Geçersiz kılınabilecek sınıflarda virtualve kullanmalısınız override. virtualAlt sınıflar tarafından geçersiz kılınabilen yöntemler için kullanın ve overridebu tür virtualyöntemleri geçersiz kılması gereken yöntemler için kullanın .


0

Yukarıda bahsettiğim kodun aynısını java'da bazı değişiklikler dışında yazdım ve haricinde iyi çalıştı. Temel sınıfın yöntemi geçersiz kılınır ve bu nedenle görüntülenen çıktı "Ben Öğretmenim" olur.

Sebep: Aslında türetilmiş sınıfın referansını içeren (türetilmiş sınıfın örneğini gönderme yeteneğine sahip olan) temel sınıfın bir başvurusunu oluştururken. Ve bildiğimiz gibi, örneğin her zaman önce yöntemlerine baktığını, orada bulursa onu çalıştırır ve orada tanımı bulamazsa hiyerarşide yükselir.

public class inheritance{

    public static void main(String[] args){

        Person person = new Teacher();
        person.ShowInfo();
    }
}

class Person{

    public void ShowInfo(){
        System.out.println("I am Person");
    }
}

class Teacher extends Person{

    public void ShowInfo(){
        System.out.println("I am Teacher");
    }
}

0

Keith S.'nin mükemmel gösterimi ve diğer herkesin kaliteli yanıtları üzerine inşa ederek ve mükemmel bütünlük uğruna devam edip bunun nasıl çalıştığını göstermek için açık arayüz uygulamalarını fırlatalım. Aşağıdakileri düşünün:

ad alanı LinqConsoleApp {

class Program
{

    static void Main(string[] args)
    {


        Person person = new Teacher();
        Console.Write(GetMemberName(() => person) + ": ");
        person.ShowInfo();

        Teacher teacher = new Teacher();
        Console.Write(GetMemberName(() => teacher) + ": ");
        teacher.ShowInfo();

        IPerson person1 = new Teacher();
        Console.Write(GetMemberName(() => person1) + ": ");
        person1.ShowInfo();

        IPerson person2 = (IPerson)teacher;
        Console.Write(GetMemberName(() => person2) + ": ");
        person2.ShowInfo();

        Teacher teacher1 = (Teacher)person1;
        Console.Write(GetMemberName(() => teacher1) + ": ");
        teacher1.ShowInfo();

        Person person4 = new Person();
        Console.Write(GetMemberName(() => person4) + ": ");
        person4.ShowInfo();

        IPerson person3 = new Person();
        Console.Write(GetMemberName(() => person3) + ": ");
        person3.ShowInfo();

        Console.WriteLine();

        Console.ReadLine();

    }

    private static string GetMemberName<T>(Expression<Func<T>> memberExpression)
    {
        MemberExpression expressionBody = (MemberExpression)memberExpression.Body;
        return expressionBody.Member.Name;
    }

}
interface IPerson
{
    void ShowInfo();
}
public class Person : IPerson
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Person == " + this.GetType());
    }
    void IPerson.ShowInfo()
    {
        Console.WriteLine("I am interface Person == " + this.GetType());
    }
}
public class Teacher : Person, IPerson
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Teacher == " + this.GetType());
    }
}

}

İşte çıktı:

kişi: Ben Kişi == LinqConsoleApp.Teacher

öğretmen: Ben Öğretmenim == LinqConsoleApp.Teacher

person1: Ben Öğretmenim == LinqConsoleApp.Teacher

person2: Ben Öğretmenim == LinqConsoleApp.Teacher

öğretmen1: Ben Öğretmenim == LinqConsoleApp.Teacher

person4: Ben Kişi == LinqConsoleApp.Person

person3: Arayüzeyim Person == LinqConsoleApp.Person

Dikkat edilmesi gereken iki nokta:
Teacher.ShowInfo () yöntemi yeni anahtar kelimeyi atlar. Yeni atlandığında, yöntem davranışı, yeni anahtar kelimenin açıkça tanımlanmış olmasıyla aynıdır.

Geçersiz kılma anahtar sözcüğünü yalnızca sanal anahtar sözcük ile birlikte kullanabilirsiniz. Temel sınıf yöntemi sanal olmalıdır. Veya soyut, bu durumda sınıf da soyut olmalıdır.

Kişi, ShowInfo'nun temel uygulamasını alır çünkü Teacher sınıfı temel uygulamayı geçersiz kılamaz (sanal bildirim yok) ve kişi .GetType (Teacher) olduğundan Teacher sınıfının uygulamasını gizler.

öğretmen, ShowInfo'nun türetilmiş Öğretmen uygulamasını alır çünkü öğretmen Typeof (Öğretmen) olduğundan ve Kişi kalıtım düzeyinde değildir.

person1, .GetType (Öğretmen) olduğundan türetilmiş Öğretmen uygulamasını alır ve ima edilen yeni anahtar kelime temel uygulamayı gizler.

person2, IPerson'ı uygulamasına ve IPerson'a açıkça atanmasına rağmen, türetilmiş Öğretmen uygulamasını da alır. Bunun nedeni yine Teacher sınıfının IPerson.ShowInfo () yöntemini açıkça uygulamamasıdır.

Teacher1 ayrıca .GetType (Teacher) olduğu için türetilmiş Teacher uygulamasını alır.

Yalnızca person3, ShowInfo'nun IPerson uygulamasını alır çünkü yalnızca Person sınıfı yöntemi açıkça uygular ve person3, IPerson türünün bir örneğidir.

Bir arabirimi açıkça uygulamak için, hedef arabirim türünün bir var örneğini bildirmeniz ve bir sınıfın arabirim üyelerini açıkça uygulaması (tam olarak nitelendirmesi) gerekir.

Kişi4'ün bile IPerson.ShowInfo uygulamasını almadığına dikkat edin. Bunun nedeni, kişi4'ün .GetType (Kişi) olmasına ve Kişinin IPerson uygulamasına rağmen, kişi4'ün bir IPerson örneği olmamasıdır.


Kodu doğru şekilde biçimlendirmenin biraz zorluk teşkil ettiğini görüyorum. Şu anda
güzelleştirmek

0

Körü körüne başlatmak ve kodun tekrarını azaltmak için LinQPad örneği Bunu yapmaya çalıştığınızı düşünüyorum.

void Main()
{
    IEngineAction Test1 = new Test1Action();
    IEngineAction Test2 = new Test2Action();
    Test1.Execute("Test1");
    Test2.Execute("Test2");
}

public interface IEngineAction
{
    void Execute(string Parameter);
}

public abstract class EngineAction : IEngineAction
{
    protected abstract void PerformAction();
    protected string ForChildren;
    public void Execute(string Parameter)
    {  // Pretend this method encapsulates a 
       // lot of code you don't want to duplicate 
      ForChildren = Parameter;
      PerformAction();
    }
}

public class Test1Action : EngineAction
{
    protected override void PerformAction()
    {
        ("Performed: " + ForChildren).Dump();
    }
}

public class Test2Action : EngineAction
{
    protected override void PerformAction()
    {
        ("Actioned: " + ForChildren).Dump();
    }
}
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.