Bagaimana cara memanggil base.base.method ()?


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();
    }
}

Hasilnya adalah:

Dipanggil dari Special Derived.
Dipanggil dari Berasal. / * ini tidak diharapkan * /
Dipanggil dari Base.

Bagaimana saya bisa menulis ulang kelas SpecialDerived sehingga metode kelas menengah "Derived" tidak dipanggil?

UPDATE: Alasan mengapa saya ingin mewarisi dari Derived bukannya Base is Derived class berisi banyak implementasi lainnya. Karena saya tidak bisa melakukannya di base.base.method()sini, saya rasa cara terbaik adalah melakukan hal berikut?

// Tidak dapat mengubah kode sumber

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.");
    }
}

Mengedit untuk mencocokkan pembaruan Anda.
JoshJordan

Jawaban:


106

Hanya ingin menambahkan ini di sini, karena orang masih kembali ke pertanyaan ini bahkan setelah sekian waktu. Tentu saja ini praktik yang buruk, tetapi masih mungkin (pada prinsipnya) untuk melakukan apa yang diinginkan penulis dengan:

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
Saya sangat menyukai jawaban ini karena pertanyaannya bukanlah apakah dianjurkan atau tidak atau apakah itu ide yang bagus atau tidak, pertanyaannya adalah apakah ada cara untuk melakukannya dan jika demikian apa cara itu.
Shavais

3
Sangat berguna ketika Anda harus berurusan dengan frameworks / libs yang tidak memiliki ekstensibilitas. Sebagai contoh, saya tidak pernah perlu menggunakan solusi seperti itu untuk NHibernate yang sangat dapat dikembangkan. Tetapi untuk berurusan dengan Identitas Asp.Net, Kerangka Kerja Entitas, Asp.Net Mvc, saya secara teratur akhirnya menggunakan peretasan semacam itu untuk menangani fitur-fitur mereka yang hilang atau perilaku kode keras yang tidak sesuai untuk kebutuhan saya.
Frédéric

2
Terima kasih untuk itu! Saya ingin mengatakan kepada orang lain untuk berhenti mengutip pedoman dalam menanggapi pertanyaan. Itu ditanyakan karena alasan, bukan omelan. Jika Anda tidak tahu, jangan menjawabnya! Saya juga ingin mendorong semua orang untuk meledakkan kode-polisi jadi mungkin itu akan membuat mereka enggan memposting non-jawaban. Jika setelah menjawab pertanyaan Anda merasa terdorong untuk mengutip panduan, lanjutkan dan sebutkan.
DanW

1
Bagaimana jika kita membutuhkan nilai kembalian dari fungsi yang mendasari?
Perkins

2
Lupakan, cari tahu. Transmisikan ke Func<stuff>alih-alihAction
Perkins

92

Ini adalah praktik pemrograman yang buruk, dan tidak diperbolehkan di C #. Ini praktik pemrograman yang buruk karena

  • Detail dari grandbase adalah detail implementasi dari base; Anda seharusnya tidak bergantung pada mereka. Kelas dasar menyediakan abstraksi di atas grandbase; Anda harus menggunakan abstraksi itu, bukan membuat jalan pintas untuk menghindarinya.

  • Untuk mengilustrasikan contoh spesifik dari poin sebelumnya: jika diizinkan, pola ini akan menjadi cara lain untuk membuat kode rentan terhadap kegagalan kelas-basis-rapuh. Misalkan Cberasal dari Byang berasal A. Kode Cdigunakan base.baseuntuk memanggil metode A. Kemudian penulis Bmenyadari bahwa mereka telah menempatkan terlalu banyak peralatan di kelas B, dan pendekatan yang lebih baik adalah membuat kelas menengah B2yang berasal dari A, dan Bdari B2. Setelah perubahan itu, code in Cmemanggil metode in B2, bukan in A, karena Cpenulis berasumsi bahwa detail implementasi dari B, yaitu kelas basis langsungnya adalahA, tidak akan pernah berubah. Banyak keputusan desain di C # adalah untuk mengurangi kemungkinan berbagai jenis kegagalan dasar yang rapuh; keputusan untuk membuat base.baseilegal sepenuhnya mencegah rasa khusus dari pola kegagalan itu.

  • Anda berasal dari basis Anda karena Anda menyukai apa yang dilakukannya dan ingin menggunakan kembali serta memperluasnya. Jika Anda tidak menyukai apa yang dilakukannya dan ingin mengatasinya daripada mengerjakannya, lalu mengapa Anda berasal darinya? Dapatkan dari grandbase sendiri jika itu adalah fungsionalitas yang ingin Anda gunakan dan kembangkan.

  • Basis mungkin memerlukan invarian tertentu untuk tujuan keamanan atau konsistensi semantik yang dipertahankan oleh detail tentang bagaimana basis menggunakan metode grandbase. Mengizinkan kelas turunan dari basis untuk melewati kode yang mempertahankan invarian tersebut dapat membuat basis menjadi tidak konsisten, keadaan rusak.


4
@ Jadoon: Kemudian lebih memilih komposisi daripada warisan. Kelas Anda dapat mengambil instance dari Base atau GrandBase, dan kelas tersebut kemudian dapat menangguhkan fungsionalitas ke instance tersebut.
Eric Lippert

4
@BlackOverlord: Karena Anda sangat menyukai topik ini, mengapa tidak menulis jawaban Anda sendiri untuk pertanyaan berusia tujuh tahun ini? Dengan begitu kita semua bisa mendapatkan keuntungan dari kebijaksanaan Anda tentang subjek ini, dan Anda kemudian akan menulis dua jawaban di StackOverflow, menggandakan total kontribusi Anda. Itu sama-sama menguntungkan.
Eric Lippert

4
@ Eric Lippert: Ada dua alasan mengapa saya tidak menulis jawaban saya sendiri: pertama, saya tidak tahu bagaimana melakukannya, itulah mengapa saya menemukan topik ini. Kedua, ada jawaban komprehensif oleh Evk di halaman ini.
BlackOverlord

3
@DanW: Saya tahu jawabannya; ini adalah kalimat pertama dari jawaban saya: fitur yang diinginkan tidak diperbolehkan di C # karena ini adalah praktik pemrograman yang buruk . Bagaimana Anda melakukan ini di C #? Kamu tidak. Gagasan bahwa saya mungkin tidak tahu jawaban atas pertanyaan ini adalah hal yang lucu, tetapi kami akan membiarkannya berlalu tanpa komentar lebih lanjut. Sekarang, jika Anda merasa jawaban ini tidak memuaskan, mengapa tidak menulis jawaban Anda sendiri yang menurut Anda lebih baik? Dengan begitu kami semua belajar dari kebijaksanaan dan pengalaman Anda, dan Anda juga akan melipatgandakan jumlah jawaban yang Anda kirimkan tahun ini.
Eric Lippert

11
@EricLippert bagaimana jika kode dasar cacat dan perlu diganti seperti kontrol pihak ketiga? Dan implementasinya termasuk panggilan ke grandbase? Untuk aplikasi dunia nyata, ini mungkin bersifat kritis dan perlu perbaikan terbaru sehingga menunggu vendor pihak ke-3 mungkin bukan pilihan. Praktik buruk vs realitas lingkungan produksi.
Shiv

22

Anda tidak bisa dari C #. Dari IL, ini sebenarnya didukung. Anda dapat melakukan panggilan non-virt ke salah satu kelas orang tua Anda ... tapi tolong jangan. :)


11

Jawabannya (yang saya tahu bukan yang Anda cari) adalah:

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

Sebenarnya, Anda hanya memiliki interaksi langsung dengan kelas yang Anda warisi. Pikirkan kelas itu sebagai lapisan - menyediakan sebanyak atau sesedikit itu atau fungsionalitas induknya seperti yang diinginkannya ke kelas turunannya.

EDIT:

Pengeditan Anda berfungsi, tetapi saya pikir saya akan menggunakan sesuatu seperti ini:

class Derived : Base
{
    protected bool _useBaseSay = false;

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

Tentu saja, dalam implementasi nyata, Anda mungkin melakukan sesuatu yang lebih seperti ini untuk ekstensibilitas dan pemeliharaan:

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");
    }
}

Kemudian, kelas turunan dapat mengontrol keadaan orang tuanya dengan tepat.


3
Mengapa tidak menulis fungsi yang dilindungi di Derivedmana panggilan Base.Say, sehingga dapat dipanggil SpecialDerived? Lebih sederhana, bukan?
nawfal

7

Mengapa tidak melemparkan kelas anak ke kelas induk tertentu dan meminta implementasi spesifik itu? Ini adalah situasi kasus khusus dan solusi kasus khusus harus digunakan. Anda harus menggunakan newkata kunci dalam metode anak-anak.

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
Jika Anda memiliki mesin yang tidak dapat mengetahui tentang pangkalan atau anak, dan suara harus berfungsi dengan benar ketika dipanggil oleh mesin itu, suara harus diganti, bukan yang baru. Jika anak membutuhkan 99% dari fungsionalitas basis, tetapi dalam satu kasus, ia membutuhkan fungsionalitas superbase ... itulah jenis situasi yang saya pahami tentang OP yang akan dibicarakan, dalam hal ini metode ini tidak akan berfungsi. Bukan hal yang aneh dan masalah keamanan yang menyebabkan perilaku C # biasanya tidak terlalu mengkhawatirkan.
Shavais

Hanya catatan dalam kasus kontrol dan rantai panggilan peristiwa, metode ini sering dilindungi dan karenanya tidak dapat diakses seperti ini.
Shiv

2
Ini tidak menggunakan warisan, Anda juga dapat memberikan nama yang sepenuhnya unik untuk setiap Bicara.
Nick Sotiros

ya itu bukan warisan 100% tetapi menggunakan antarmuka dari orang tua
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

Anda juga dapat membuat fungsi sederhana di kelas turunan tingkat pertama, untuk memanggil fungsi grand base


Tepat sekali, dan ini mempertahankan seluruh skema abstraksi yang begitu dikhawatirkan semua orang, dan ini menyoroti bagaimana skema abstraksi terkadang lebih merepotkan daripada nilainya.
Shavais

3

2c saya untuk ini adalah untuk mengimplementasikan fungsionalitas yang Anda perlukan untuk dipanggil dalam kelas toolkit dan memanggilnya dari mana pun Anda membutuhkan:

// 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 ); }
}

Ini memerlukan pemikiran untuk mengakses hak istimewa, Anda mungkin perlu menambahkan beberapa internalmetode pengakses untuk memfasilitasi fungsionalitas tersebut.


1

Dalam kasus di mana Anda tidak memiliki akses ke sumber kelas turunan, tetapi membutuhkan semua sumber kelas turunan selain metode saat ini, maka saya akan merekomendasikan Anda juga harus melakukan kelas turunan dan memanggil implementasi kelas turunan.

Berikut ini contohnya:

//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

Seperti yang dapat dilihat dari posting sebelumnya, seseorang dapat berargumen bahwa jika fungsionalitas kelas perlu dielakkan maka ada sesuatu yang salah dalam arsitektur kelas. Itu mungkin benar, tetapi seseorang tidak selalu dapat merestrukturisasi atau memfaktor ulang struktur kelas pada proyek besar yang matang. Berbagai tingkat manajemen perubahan mungkin menjadi satu masalah, tetapi untuk menjaga fungsionalitas yang ada tetap beroperasi sama setelah pemfaktoran ulang tidak selalu merupakan tugas yang sepele, terutama jika batasan waktu berlaku. Pada proyek yang matang, dapat menjadi usaha yang cukup berat untuk menjaga berbagai uji regresi agar tidak lolos setelah restrukturisasi kode; sering ada "keanehan" yang muncul. Kami memiliki masalah serupa dalam beberapa kasus, fungsi yang diwariskan tidak boleh dijalankan (atau harus melakukan sesuatu yang lain). Pendekatan yang kami ikuti di bawah ini, adalah meletakkan kode dasar yang perlu dikecualikan dalam fungsi virtual terpisah. Fungsi ini kemudian dapat diganti di kelas turunan dan fungsionalitasnya dikecualikan atau diubah. Dalam contoh ini "Teks 2" dapat dicegah dari keluaran di kelas turunan.

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

Tampaknya ada banyak pertanyaan seputar mewarisi metode anggota dari Kelas Kakek, menimpanya di Kelas kedua, lalu memanggil metode itu lagi dari Kelas Cucu. Mengapa tidak mewarisi anggota dari kakek nenek sampai ke cucu?

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"

Sepertinya sederhana .... cucu mewarisi metode kakek-nenek di sini. Pikirkan tentang itu ..... begitulah "Object" dan anggotanya seperti ToString () diwarisi ke semua kelas di C #. Saya pikir Microsoft belum melakukan pekerjaan yang baik dalam menjelaskan warisan dasar. Terlalu banyak fokus pada polimorfisme dan implementasi. Ketika saya menggali dokumentasi mereka, tidak ada contoh dari ide yang sangat mendasar ini. :(


-2

Jika Anda ingin mengakses data kelas dasar Anda harus menggunakan kata kunci "ini" atau Anda menggunakan kata kunci ini sebagai referensi untuk kelas.

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;
        }
    }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.