Base.base.method () nasıl çağrılır?


127
// Cannot change source code
class Base
{
    public virtual void Say()
    {
        Console.WriteLine("Called from Base.");
    }
}

// Cannot change source code
class Derived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Derived.");
        base.Say();
    }
}

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

class Program
{
    static void Main(string[] args)
    {
        SpecialDerived sd = new SpecialDerived();
        sd.Say();
    }
}

Sonuç:

Özel Türetilmiş Aradı.
Türetildi. / * bu beklenmiyor * /
Üssünden çağrıldı.

SpecialDerived sınıfını orta sınıf "Derived" yönteminin çağrılmaması için nasıl yeniden yazabilirim?

GÜNCELLEME: Base yerine Derived sınıfından miras almak istememin nedeni, Derived sınıfı birçok başka uygulama içeriyor. base.base.method()Burada yapamayacağıma göre , sanırım en iyi yol aşağıdakileri yapmak?

// Kaynak kodu değiştirilemez

class Derived : Base
{
    public override void Say()
    {
        CustomSay();

        base.Say();
    }

    protected virtual void CustomSay()
    {
        Console.WriteLine("Called from Derived.");
    }
}

class SpecialDerived : Derived
{
    /*
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
    */

    protected override void CustomSay()
    {
        Console.WriteLine("Called from Special Derived.");
    }
}

Güncellemenize uyacak şekilde düzenleme.
JoshJordan

Yanıtlar:


106

Bunu buraya eklemek istiyorum, çünkü insanlar pek çok kez bu soruya hala geri dönüyorlar. Elbette kötü bir uygulamadır, ancak yazarın istediği şeyi yapmak (ilke olarak) hala mümkündür:

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        var ptr = typeof(Base).GetMethod("Say").MethodHandle.GetFunctionPointer();            
        var baseSay = (Action)Activator.CreateInstance(typeof(Action), this, ptr);
        baseSay();            
    }
}

46
Bu cevabı gerçekten beğendim çünkü soru tavsiye edilip edilmeyeceği ya da iyi bir fikir olup olmadığı değildi, soru bunu yapmanın bir yolu olup olmadığı ve eğer öyleyse bu yolun ne olduğuydu.
Shavais

3
Genişletilebilirlikten yoksun çerçevelerle / kitaplarla uğraşmanız gerektiğinde özellikle yararlıdır. Örneğin, NHibernate için yüksek oranda genişletilebilir olan bu tür çözümlere başvurmak zorunda kalmadım. Ancak Asp.Net Identity, Entity Framework, Asp.Net Mvc ile uğraşmak için, eksik özellikleri veya ihtiyaçlarıma uygun olmayan sabit kodlanmış davranışları ele almak için düzenli olarak bu tür hack'leri kullanıyorum.
Frédéric

2
Bunun için teşekkürler! Diğer insanlara, bir soruya yanıt olarak yönergelerden alıntı yapmayı bırakmalarını belirtmek isterim. Azarlama değil, bir sebep istendi. Bilmiyorsan, cevap verme! Ayrıca herkesi kod polisini patlatmaya teşvik etmek isterim, böylece belki bu onları cevapsız mesajlar yayınlamaktan caydırabilir. Bir soruyu yanıtladıktan sonra, yönergelerden alıntı yapmaya mecbur hissediyorsanız, devam edin ve bundan bahsedin.
DanW

1
Ya temeldeki işlevin dönüş değerine ihtiyacımız olursa?
Perkins

2
Boşver, anladım. Yayınla Func<stuff>yerineAction
Perkins

92

Bu kötü bir programlama uygulamasıdır ve C # 'da izin verilmez. Kötü bir programlama uygulaması çünkü

  • Grandbase'in detayları tabanın uygulama detaylarıdır; onlara güvenmemelisin. Temel sınıf, temelin üzerinde bir soyutlama sağlıyor; bu soyutlamayı kullanmalısınız, bundan kaçınmak için bir baypas inşa etmemelisiniz.

  • Önceki noktanın belirli bir örneğini göstermek için: izin verilirse, bu model kodu kırılgan temel sınıf başarısızlıklarına duyarlı hale getirmenin başka bir yolu olacaktır. Varsayalım ki C, Bhangisinden türemiştir A. Kod Ckullanım base.baseyöntemini çağırmak için A. O zaman kitabın yazarı, Bsınıfa çok fazla ekipman koyduklarını anlar Bve daha iyi bir yaklaşım, B2türetilen Ave Btüretilen orta sınıf yapmaktır B2. Bu değişiklikten sonra, kod içinde Cbir yöntem çağırıyor B2, içinde değil A, çünkü asla değişmeyecek. C # 'daki birçok tasarım kararı, çeşitli türlerdeki kırılgan temel arızalarının olasılığını azaltmak içindir; karar vermeC yazarı, uygulama ayrıntılarının B, yani doğrudan temel sınıfınınAbase.base yasa dışı, bu başarısızlık modelinin bu özel çeşidini tamamen engeller.

  • Kendi üssünüzden türetmişsiniz çünkü yaptığı şeyi seviyorsunuz ve onu yeniden kullanmak ve genişletmek istiyorsunuz. Yaptığı şeyi beğenmiyorsan ve onunla çalışmaktansa etrafında çalışmak istiyorsan, o zaman neden ilk etapta ondan türettin? Kullanmak ve genişletmek istediğiniz işlevsellik buysa, büyük tabandan kendiniz türetin.

  • Taban, temelin temelin yöntemlerini nasıl kullandığının ayrıntılarıyla sürdürülen güvenlik veya anlamsal tutarlılık amaçları için belirli değişmezleri gerektirebilir. Tabanın türetilmiş bir sınıfının, bu değişmezleri koruyan kodu atlamasına izin vermek, tabanı tutarsız, bozuk bir duruma sokabilir.


4
@Jadoon: O zaman kompozisyonu mirasa tercih edin. Sınıfınız Base veya GrandBase'in bir örneğini alabilir ve ardından sınıf, işlevselliği örneğe erteleyebilir.
Eric Lippert

4
@BlackOverlord: Bu konuda güçlü hissettiğine göre, neden bu yedi yaşındaki soruya kendi cevabını yazmıyorsun? Bu şekilde hepimiz bu konudaki bilgeliğinizden yararlanırız ve daha sonra StackOverflow'da toplam katkınızı ikiye katlayarak iki yanıt yazmış olursunuz. Bu bir kazan-kazan.
Eric Lippert

4
@Eric Lippert: Kendi cevabımı yazmamamın iki nedeni var: Birincisi, nasıl yapılacağını bilmiyordum, bu yüzden bu konuyu buldum. İkincisi, bu sayfada Evk'in kapsamlı bir cevabı var.
BlackOverlord

3
@DanW: Cevabı biliyorum; cevabımın ilk cümlesi: C # 'da istenen özelliğe izin verilmiyor çünkü kötü bir programlama uygulaması . Bunu C # ile nasıl yaparsınız? Yapmıyorsun. Bu sorunun cevabını bilmeyebileceğim düşüncesi eğlenceli bir fikir, ancak daha fazla yorum yapmadan bunun geçmesine izin vereceğiz. Şimdi, bu cevabı tatmin edici bulmuyorsanız, neden daha iyi bir iş çıkardığını düşündüğünüz kendi cevabınızı yazmıyorsunuz? Bu şekilde hepimiz bilgeliğinizden ve deneyiminizden öğreniriz ve bu yıl gönderdiğiniz cevapların sayısını ikiye katlarsınız.
Eric Lippert

11
@EricLippert ya temel kod kusurluysa ve bir 3. taraf kontrolü gibi geçersiz kılınması gerekiyorsa? Ve uygulama grandbase'e bir çağrı içeriyor mu? Gerçek dünya uygulamaları için, kritik nitelikte olabilir ve düzeltmeye ihtiyaç duyabilir, bu nedenle 3. taraf satıcıyı beklemek bir seçenek olmayabilir. Üretim ortamlarının gerçekliğine karşı kötü uygulama.
Shiv 18

22

C # ile yapamazsınız. IL'den bu aslında desteklenmektedir. Ebeveyn sınıflarınızdan herhangi birine erdem dışı bir arama yapabilirsiniz ... ama lütfen yapma. :)


11

Cevap (aradığınız şey olmadığını biliyorum):

class SpecialDerived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

Gerçek şu ki, sadece miras aldığınız sınıfla doğrudan etkileşime sahipsiniz. Bu sınıfı bir katman olarak düşünün - türetilmiş sınıflarına istediği kadar çok veya az onun veya ebeveyninin işlevselliğini sağlar.

DÜZENLE:

Düzenlemeniz işe yarıyor, ancak sanırım şöyle bir şey kullanacağım:

class Derived : Base
{
    protected bool _useBaseSay = false;

    public override void Say()
    {
        if(this._useBaseSay)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}

Elbette, gerçek bir uygulamada, genişletilebilirlik ve sürdürülebilirlik için buna benzer bir şey yapabilirsiniz:

class Derived : Base
{
    protected enum Mode
    {
        Standard,
        BaseFunctionality,
        Verbose
        //etc
    }

    protected Mode Mode
    {
        get; set;
    }

    public override void Say()
    {
        if(this.Mode == Mode.BaseFunctionality)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}

Ardından, türetilmiş sınıflar, ebeveynlerinin durumunu uygun şekilde kontrol edebilir.


3
Neden sadece bir korumalı işlevi yazmak değil Derivedhangi aramaların Base.Sayo çağrılabilir böylece, SpecialDerived? Daha basit, değil mi?
nawfal

7

Neden çocuk sınıfı belirli bir ebeveyn sınıfa atıp o zaman belirli uygulamayı başlatmıyorsunuz? Bu özel bir durumdur ve özel bir durum çözümü kullanılmalıdır. newYine de , anahtar kelimeyi çocuk yöntemlerinde kullanmanız gerekecektir .

public class SuperBase
{
    public string Speak() { return "Blah in SuperBase"; }
}

public class Base : SuperBase
{
    public new string Speak() { return "Blah in Base"; }
}

public class Child : Base
{
    public new string Speak() { return "Blah in Child"; }
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Child childObj = new Child();

        Console.WriteLine(childObj.Speak());

        // casting the child to parent first and then calling Speak()
        Console.WriteLine((childObj as Base).Speak()); 

        Console.WriteLine((childObj as SuperBase).Speak());
    }
}

2
Üs veya çocuk hakkında bilgi sahibi olmayan bir motorunuz varsa ve konuşmanın o motor tarafından çağrıldığında doğru çalışması gerekiyorsa, konuşmanın yeni değil, geçersiz kılma olması gerekir. Çocuk, temelin işlevselliğinin% 99'una ihtiyaç duyuyorsa, ancak tek konuşma durumunda, süper üssün işlevselliğine ihtiyaç duyuyorsa ... bu, OP'nin bahsetmesini anladığım türden bir durumdur, bu durumda bu yöntem işe yaramaz. Bu alışılmadık bir durum değildir ve C # 'ın davranışına neden olan güvenlik endişeleri genellikle pek ilgili değildir.
Shavais

Kontroller ve olay çağrısı zincirleri söz konusu olduğunda sadece bir not, yöntemler genellikle korunur ve bu nedenle bu şekilde erişilemez.
Shiv 18

2
Bu miras kullanmaz, her Speak'e tamamen benzersiz bir ad da verebilirsiniz.
Nick Sotiros

evet,% 100 kalıtım değil ama ebeveynin arayüzünü kullanıyor
Kruczkowski

5
public class A
{
    public int i = 0;
    internal virtual void test()
    {
        Console.WriteLine("A test");
    }
}

public class B : A
{
    public new int i = 1;
    public new void test()
    {
        Console.WriteLine("B test");
    }
}

public class C : B
{
    public new int i = 2;
    public new void test()
    {
        Console.WriteLine("C test - ");
        (this as A).test(); 
    }
}

4

Ayrıca, birinci düzey türetilmiş sınıfta, büyük temel işlevi çağırmak için basit bir işlev de yapabilirsiniz.


Aynen öyle ve bu, herkesin endişelendiği tüm soyutlama şemasını koruyor ve soyutlama şemalarının bazen değerinden daha fazla sorun olduğunu vurguluyor.
Shavais

3

Bunun için benim 2c'm, bir araç takımı sınıfında çağrılmak için ihtiyaç duyduğunuz işlevselliği uygulamak ve ihtiyacınız olan her yerden çağırmaktır:

// Util.cs
static class Util 
{
    static void DoSomething( FooBase foo ) {}
}

// FooBase.cs
class FooBase
{
    virtual void Do() { Util.DoSomething( this ); }
}


// FooDerived.cs
class FooDerived : FooBase
{
    override void Do() { ... }
}

// FooDerived2.cs
class FooDerived2 : FooDerived
{
    override void Do() { Util.DoSomething( this ); }
}

Bu, erişim ayrıcalığına ilişkin biraz düşünmeyi gerektirir internal, işlevselliği kolaylaştırmak için bazı erişimci yöntemleri eklemeniz gerekebilir .


1

Türetilmiş sınıf kaynağına erişiminizin olmadığı, ancak mevcut yöntemin yanı sıra türetilmiş sınıfın tüm kaynağına ihtiyaç duyduğunuz durumlarda, türetilmiş bir sınıf yapmanızı ve türetilmiş sınıfın uygulamasını çağırmanızı tavsiye ederim.

İşte bir örnek:

//No access to the source of the following classes
public class Base
{
     public virtual void method1(){ Console.WriteLine("In Base");}
}
public class Derived : Base
{
     public override void method1(){ Console.WriteLine("In Derived");}
     public void method2(){ Console.WriteLine("Some important method in Derived");}
}

//Here should go your classes
//First do your own derived class
public class MyDerived : Base
{         
}

//Then derive from the derived class 
//and call the bass class implementation via your derived class
public class specialDerived : Derived
{
     public override void method1()
     { 
          MyDerived md = new MyDerived();
          //This is actually the base.base class implementation
          MyDerived.method1();  
     }         
}

0

Önceki gönderilerden görülebileceği gibi, sınıf işlevselliğinin atlatılması gerekiyorsa, o zaman sınıf mimarisinde bir şeylerin yanlış olduğu iddia edilebilir. Bu doğru olabilir, ancak büyük ve olgun bir projede sınıf yapısı her zaman yeniden yapılandırılamaz veya yeniden düzenlenemez. Değişiklik yönetiminin çeşitli seviyeleri bir sorun olabilir, ancak mevcut işlevselliğin yeniden düzenleme sonrasında aynı şekilde çalışmasını sağlamak her zaman önemsiz bir görev değildir, özellikle de zaman kısıtlamaları varsa. Olgun bir projede, bir kodun yeniden yapılandırılmasının ardından çeşitli regresyon testlerinin geçmesini engellemek oldukça büyük bir girişim olabilir; genellikle ortaya çıkan belirsiz "tuhaflıklar" vardır. Bazı durumlarda, miras alınan işlevselliğin yürütülmemesi (veya başka bir şey gerçekleştirmesi) gereken benzer bir sorun yaşadık. Aşağıda izlediğimiz yaklaşım, dışlanması gereken temel kodu ayrı bir sanal işleve koymaktı. Bu işlev daha sonra türetilmiş sınıfta geçersiz kılınabilir ve işlevsellik hariç tutulur veya değiştirilebilir. Bu örnekte "Metin 2" nin türetilmiş sınıfta çıktı vermesi engellenebilir.

public class Base
{
    public virtual void Foo()
    {
        Console.WriteLine("Hello from Base");
    }
}

public class Derived : Base
{
    public override void Foo()
    {
        base.Foo();
        Console.WriteLine("Text 1");
        WriteText2Func();
        Console.WriteLine("Text 3");
    }

    protected virtual void WriteText2Func()
    {  
        Console.WriteLine("Text 2");  
    }
}

public class Special : Derived
{
    public override void WriteText2Func()
    {
        //WriteText2Func will write nothing when 
        //method Foo is called from class Special.
        //Also it can be modified to do something else.
    }
}

0

Bir Grandparent Class'tan bir üye metodunu miras alan, ikinci bir Sınıfta geçersiz kılan ve daha sonra bir Torun Sınıfından metodunu tekrar çağıran bu soruların birçoğu var gibi görünüyor. Neden büyükbabanın üyelerini torunlarına miras bırakmıyorsunuz?

class A
{
    private string mystring = "A";    
    public string Method1()
    {
        return mystring;
    }
}

class B : A
{
    // this inherits Method1() naturally
}

class C : B
{
    // this inherits Method1() naturally
}


string newstring = "";
A a = new A();
B b = new B();
C c = new C();
newstring = a.Method1();// returns "A"
newstring = b.Method1();// returns "A"
newstring = c.Method1();// returns "A"

Basit görünüyor ... torun burada büyükanne ve büyükbabanın yöntemini miras alıyor. Bir düşünün ..... "Object" ve ToString () gibi üyeleri bu şekilde C # 'daki tüm sınıflara aktarılır. Microsoft'un temel kalıtımı açıklamakta iyi bir iş çıkarmadığını düşünüyorum. Çok biçimlilik ve uygulamaya çok fazla odaklanılıyor. Belgelerine baktığımda, bu çok temel fikrin hiçbir örneği yok. :(


-2

Temel sınıf verilerine erişmek istiyorsanız, "this" anahtar kelimesini kullanmanız gerekir veya bu anahtar kelimeyi sınıf için referans olarak kullanmanız gerekir.

namespace thiskeyword
{
    class Program
    {
        static void Main(string[] args)
        {
            I i = new I();
            int res = i.m1();
            Console.WriteLine(res);
            Console.ReadLine();
        }
    }

    public class E
    {
        new public int x = 3;
    }

    public class F:E
    {
        new public int x = 5;
    }

    public class G:F
    {
        new public int x = 50;
    }

    public class H:G
    {
        new public int x = 20;
    }

    public class I:H
    {
        new public int x = 30;

        public int m1()
        {
           // (this as <classname >) will use for accessing data to base class

            int z = (this as I).x + base.x + (this as G).x + (this as F).x + (this as E).x; // base.x refer to H
            return z;
        }
    }
}
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.