Mengapa metode statis tidak bisa ditimpa?

13

Dalam menjawab pertanyaan ini , konsensus umum adalah bahwa metode statis tidak dimaksudkan untuk ditimpa (dan dengan demikian fungsi statis di C # tidak boleh virtual atau abstrak). Namun, ini bukan hanya kasus di C #; Java juga melarang ini dan C ++ sepertinya juga tidak suka. Namun, saya dapat memikirkan banyak contoh fungsi statis yang ingin saya timpa dalam kelas anak (misalnya, metode pabrik). Sementara secara teori, ada cara untuk menyiasatinya, tidak ada yang bersih atau sederhana.

Mengapa fungsi statis tidak bisa ditimpa?

PixelArtDragon
sumber
4
Delphi mendukung metode kelas , yang sangat mirip dengan metode statis dan dapat diganti. Mereka melewati sebuah selfpointer yang menunjuk ke kelas dan bukan ke instance kelas.
CodesInChaos
Salah satu definisi statis adalah: kurang perubahan. Saya ingin tahu mengapa Anda membuat fungsi statis yang ingin Anda timpa.
JeffO
4
@ Jeffoff: Bahwa definisi bahasa Inggris "statis" sama sekali tidak ada hubungannya dengan semantik unik fungsi anggota statis.
Lightness Races with Monica
5
Pertanyaan Anda adalah "mengapa metode statis harus menggunakan pengiriman yang diketik secara statis alih-alih pengiriman tunggal-virtual ?" Ketika Anda mengucapkan pertanyaan seperti itu, saya pikir itu menjawab sendiri.
Eric Lippert
1
@ EricLippert Pertanyaannya lebih baik diucapkan sebagai "Mengapa C # menawarkan metode statis daripada metode kelas?", Tapi itu masih merupakan pertanyaan yang valid.
CodesInChaos

Jawaban:

13

Dengan metode statis, tidak ada objek untuk memberikan kontrol yang tepat terhadap mekanisme override.

Mekanisme metode virtual kelas / instance yang normal memungkinkan untuk mengontrol override yang disetel dengan halus sebagai berikut: setiap objek nyata adalah instance dari satu kelas. Kelas itu menentukan perilaku override; selalu mendapatkan celah pertama pada metode virtual. Kemudian dapat memilih untuk memanggil metode induk pada waktu yang tepat untuk penerapannya. Setiap metode induk kemudian juga mendapat giliran untuk memanggil metode induknya. Ini menghasilkan kaskade bagus dari pemanggilan orang tua, yang memenuhi salah satu konsep penggunaan kembali kode yang dikenal sebagai orientasi objek. (Di sini kode dasar / kelas super digunakan kembali dengan cara yang relatif kompleks; gagasan ortogonal lain tentang penggunaan kembali kode dalam OOP hanya memiliki banyak objek dari kelas yang sama.)

Kelas dasar dapat digunakan kembali oleh berbagai subclass, dan masing-masing dapat hidup berdampingan dengan nyaman. Setiap kelas yang digunakan untuk instantiate objek mendiktekan perilakunya sendiri, secara damai dan bersamaan hidup berdampingan dengan yang lain. Klien memiliki kendali atas perilaku mana yang diinginkan dan kapan dengan memilih kelas mana yang akan digunakan untuk membuat instance objek dan memberikan kepada yang lain sesuai keinginan.

(Ini bukan mekanisme yang sempurna, karena kita selalu dapat mengidentifikasi kemampuan yang tidak didukung, tentu saja, itulah sebabnya pola seperti metode pabrik dan injeksi ketergantungan diletakkan di atas.)

Jadi, jika kita membuat kemampuan menimpa untuk statika tanpa mengubah apa pun, kita akan mengalami kesulitan memesan penggantian. Akan sulit untuk mendefinisikan konteks terbatas untuk penerapan override, sehingga Anda akan mendapatkan override secara global daripada lebih lokal seperti dengan objek. Tidak ada objek instan untuk mengubah perilaku. Jadi, jika seseorang memanggil metode statis yang kebetulan telah ditimpa oleh kelas lain, haruskah override mendapatkan kontrol atau tidak? Jika ada beberapa penggantian semacam itu, siapa yang mendapat kendali lebih dulu? kedua? Dengan mengesampingkan objek instan, semua pertanyaan ini memiliki jawaban yang bermakna dan beralasan, tetapi dengan statika tidak.

Penggantian untuk statika akan sangat kacau, dan, hal-hal seperti ini telah dilakukan sebelumnya.

Misalnya, Mac OS System 7 dan sebelumnya menggunakan mekanisme patch trap untuk memperluas sistem dengan mendapatkan kontrol panggilan sistem buatan aplikasi sebelum sistem operasi. Anda bisa menganggap tabel tambalan panggilan sistem sebagai array penunjuk fungsi, mirip dengan vtable untuk objek contoh, kecuali bahwa itu adalah tabel global tunggal.

Hal ini menyebabkan kesedihan yang tak terhingga bagi para programmer karena sifat patch yang tidak teratur. Siapa pun yang dapat menambal jebakan terakhir pada dasarnya menang, bahkan jika mereka tidak mau. Setiap patcher dari jebakan akan menangkap nilai perangkap sebelumnya untuk semacam kemampuan panggilan induk, yang sangat rapuh. Menghapus tambalan, katakan ketika Anda tidak lagi perlu tahu tentang panggilan sistem dianggap sebagai bentuk yang buruk karena Anda tidak benar-benar memiliki informasi yang diperlukan untuk menghapus tambalan Anda (jika Anda melakukannya, Anda juga akan membuka kancing tambalan lain yang telah mengikuti kamu).

Ini bukan untuk mengatakan bahwa tidak mungkin untuk membuat mekanisme untuk menimpa statika, tetapi apa yang saya lebih suka lakukan adalah mengubah bidang statis dan metode statis menjadi bidang contoh dan metode contoh metaclasses, sehingga objek normal teknik orientasi akan berlaku. Perhatikan bahwa ada sistem yang melakukan ini juga: CSE 341: Kelas Smalltalk dan metaclasses ; Lihat juga: Apa yang dimaksud dengan Smalltalk setara dengan statis Java?


Saya mencoba mengatakan bahwa Anda harus melakukan beberapa desain fitur bahasa yang serius untuk membuatnya bekerja dengan cukup baik. Sebagai contoh, pendekatan naif dilakukan, berjalan pincang, tetapi sangat bermasalah, dan bisa dibilang (yaitu saya berpendapat) secara arsitektur cacat dengan menyediakan abstraksi yang tidak lengkap dan sulit untuk digunakan.

Pada saat Anda selesai merancang fitur penggantian statis agar berfungsi dengan baik, Anda mungkin baru saja menemukan beberapa bentuk metaclasses, yang merupakan perpanjangan alami dari OOP ke / untuk metode berbasis kelas. Jadi, tidak ada alasan untuk tidak melakukan ini - dan beberapa bahasa benar-benar melakukannya. Mungkin sedikit lebih banyak persyaratan tambahan bahwa sejumlah bahasa memilih untuk tidak melakukannya.

Erik Eidt
sumber
Jika saya mengerti dengan benar: ini bukan masalah apakah secara teoritis Anda ingin atau harus melakukan sesuatu seperti ini, tetapi lebih dari itu secara programatis, penerapannya akan sulit dan belum tentu berguna?
PixelArtDragon
@ Garan, Lihat addendum saya.
Erik Eidt
1
Saya tidak membeli argumen pemesanan; Anda mendapatkan metode yang disediakan oleh kelas tempat Anda memanggil metode (atau superclass pertama yang menyediakan metode sesuai dengan linierisasi bahasa yang Anda pilih).
hobbs
1
@PierreArlaud: Tetapi this.instanceMethod()memiliki resolusi dinamis, jadi jika self.staticMethod()memiliki resolusi yang sama, yaitu dinamis, itu tidak akan menjadi metode statis lagi.
Jörg W Mittag
1
semantik utama untuk metode statis perlu ditambahkan. Metaclasses Java + hipotetis tidak perlu menambahkan apa pun! Anda hanya membuat objek kelas, dan metode statis kemudian menjadi metode instance biasa. Anda tidak perlu menambahkan sesuatu ke bahasa, karena Anda hanya menggunakan konsep yang sudah ada: objek, kelas, dan metode instance. Sebagai bonus, Anda sebenarnya dapat menghapus metode statis dari bahasa! Anda dapat menambahkan fitur dengan menghapus konsep dan kompleksitas dari bahasa!
Jörg W Mittag
9

Mengganti tergantung pada pengiriman virtual: Anda menggunakan tipe runtime dari thisparameter untuk memutuskan metode mana yang akan dipanggil. Metode statis tidak memiliki thisparameter, jadi tidak ada yang dikirim.

Beberapa bahasa, terutama Delphi dan Python, memiliki ruang lingkup "di antara" yang memungkinkan untuk ini: metode kelas. Metode kelas bukan metode contoh biasa, tetapi juga tidak statis; itu menerima selfparameter (mengejutkan, kedua bahasa memanggil thisparameter self) itu adalah referensi ke tipe objek itu sendiri, bukan ke instance dari tipe itu. Dengan nilai itu, sekarang Anda memiliki jenis yang tersedia untuk melakukan pengiriman virtual.

Sayangnya, baik JVM maupun CLR tidak memiliki apa pun yang sebanding.

Mason Wheeler
sumber
Apakah itu bahasa yang menggunakan selfyang memiliki kelas sebagai objek aktual, instantiated dengan metode yang dapat ditimpa - Smalltalk tentu saja, Ruby, Dart, Objective C, Self, Python? Dibandingkan dengan bahasa yang mengambil setelah C ++, di mana kelas bukan objek kelas pertama, bahkan jika ada beberapa akses refleksi?
Jerry101
Untuk lebih jelasnya, apakah Anda mengatakan bahwa dalam Python atau Delphi, Anda dapat mengganti metode kelas?
Erik Eidt
@ErikEidt Dengan Python, hampir semuanya bisa ditimpa. Dalam Delphi, metode kelas dapat dideklarasikan secara eksplisit virtualdan kemudian diganti dalam kelas turunan, seperti metode instance.
Mason Wheeler
Jadi, bahasa-bahasa ini memberikan semacam pengertian tentang metaclasses, bukan?
Erik Eidt
1
@ErikEidt Python memiliki metaclasses "benar". Dalam Delphi, referensi kelas adalah tipe data khusus, bukan kelas dalam dan dari dirinya sendiri.
Mason Wheeler
3

Anda bertanya

Mengapa fungsi statis tidak bisa ditimpa?

aku bertanya

Mengapa fungsi yang ingin Anda ganti menjadi statis?

Bahasa tertentu memaksa Anda untuk memulai pertunjukan dengan metode statis. Tetapi setelah itu Anda benar-benar dapat memecahkan banyak masalah tanpa metode statis sama sekali.

Beberapa orang suka menggunakan metode statis kapan saja tidak ada ketergantungan pada keadaan dalam suatu objek. Beberapa orang suka menggunakan metode statis untuk konstruksi objek lain. Beberapa orang suka menghindari metode statis sebanyak mungkin.

Tidak satu pun dari orang-orang ini yang salah.

Jika Anda perlu ditimpa, berhentilah memberi label statis. Nothings akan pecah karena Anda memiliki objek berkewarganegaraan terbang di sekitar.

candied_orange
sumber
Jika bahasa mendukung struct, maka tipe unit bisa menjadi struct berukuran nol, yang diteruskan ke metode entri logis. Penciptaan objek itu adalah detail implementasi. Dan jika kita mulai berbicara tentang detail implementasi sebagai kebenaran yang sebenarnya, maka saya pikir Anda juga perlu mempertimbangkan bahwa dalam implementasi dunia nyata, tipe berukuran nol tidak harus benar - benar instantiated (karena tidak ada instruksi yang diperlukan untuk memuat atau simpan).
Theodoros Chatzigiannakis
Dan jika Anda berbicara lebih banyak lagi "di belakang layar" (misalnya pada tingkat yang dapat dieksekusi), maka argumen lingkungan dapat didefinisikan sebagai jenis dalam bahasa dan titik masuk "nyata" dari program dapat menjadi metode contoh dari tipe itu, bekerja pada instance apa pun yang diteruskan ke program oleh OS loader.
Theodoros Chatzigiannakis
Saya pikir itu tergantung pada bagaimana Anda melihatnya. Sebagai contoh, ketika sebuah program dimulai, OS memuatnya ke dalam memori dan kemudian pergi ke alamat di mana implementasi program individu dari titik entri biner berada (misalnya _start()simbol di Linux). Dalam contoh itu, executable adalah objek (ia memiliki "tabel pengiriman" sendiri dan segalanya) dan titik masuknya adalah operasi yang dikirim secara dinamis. Oleh karena itu, titik masuk program secara inheren virtual ketika dilihat dari luar dan dapat dengan mudah dibuat terlihat virtual jika dilihat dari dalam juga.
Theodoros Chatzigiannakis
1
"Nothings akan pecah karena kamu memiliki benda berkewarganegaraan terbang di sekitar." ++++++
RubberDuck
@TheodorosChatzigiannakis Anda membuat argumen yang kuat. jika tidak ada hal lain yang meyakinkan Anda bahwa kalimat itu tidak mengilhami garis pemikiran yang saya maksudkan. Saya benar-benar mencoba memberikan izin kepada orang-orang untuk mengabaikan beberapa praktik yang lebih kebiasaan yang mereka asosiasikan secara membabi buta dengan metode statis. Jadi saya sudah perbarui. Pikiran?
candied_orange
2

Mengapa metode statis tidak bisa ditimpa?

Ini bukan pertanyaan "harus".

"Overriding" berarti "pengiriman secara dinamis ". "Metode statis" berarti "pengiriman secara statis". Jika ada sesuatu yang statis, itu tidak dapat diganti. Jika sesuatu dapat ditimpa, itu tidak statis.

Pertanyaan Anda agak mirip dengan bertanya: "Mengapa becak tidak bisa memiliki empat roda?" The definisi dari "roda tiga" adalah yang memiliki tiga roda. Jika itu adalah roda tiga, ia tidak dapat memiliki empat roda, jika ia memiliki empat roda, ia tidak dapat menjadi roda tiga. Demikian juga, definisi "metode statis" adalah bahwa ia dikirim secara statis. Jika ini adalah metode statis, itu tidak dapat dikirim secara dinamis, jika dapat dikirim secara dinamis, itu tidak bisa menjadi metode statis.

Tentu saja sangat mungkin untuk memiliki metode kelas yang dapat diganti. Atau, Anda bisa memiliki bahasa seperti Ruby, di mana kelas adalah objek sama seperti objek lainnya dan dengan demikian dapat memiliki metode contoh, yang sepenuhnya menghilangkan kebutuhan metode kelas sama sekali. (Ruby hanya memiliki satu jenis metode: metode instan. Tidak memiliki metode kelas, metode statis, konstruktor, fungsi, atau prosedur.)

Jörg W Mittag
sumber
1
"Menurut definisi" Baiklah.
candied_orange
1

dengan demikian fungsi statis di C # tidak boleh virtual atau abstrak

Dalam C #, Anda selalu memanggil anggota statis menggunakan kelas, mis . BaseClass.StaticMethod()Tidak baseObject.StaticMethod(). Jadi pada akhirnya, jika Anda ChildClassmewarisi dari BaseClassdan childObjectturunan dari ChildClass, Anda tidak akan dapat memanggil metode statis dari childObject. Anda akan selalu perlu menggunakan kelas nyata secara eksplisit, jadi static virtualitu tidak masuk akal.

Yang dapat Anda lakukan adalah mendefinisikan kembali staticmetode yang sama di kelas anak Anda, dan menggunakan newkata kunci.

class BaseClass {
    public static int StaticMethod() { return 1; }
}

class ChildClass {
    public static new int StaticMethod() { return BaseClass.StaticMethod() + 2; }
}

int result;    
var baseObj = new BaseClass();
var childObj = new ChildClass();

result = BaseClass.StaticMethod();
result = baseObj.StaticMethod(); // DOES NOT COMPILE

result = ChildClass.StaticMethod();
result = childObj.StaticMethod(); // DOES NOT COMPILE

Jika memungkinkan untuk menelepon baseObject.StaticMethod(), maka pertanyaan Anda akan masuk akal.

pengguna276648
sumber