Mengapa harus ditimpa kata kunci di depan metode abstrak saat kita menerapkannya dalam kelas anak?

9

Ketika kita membuat kelas yang mewarisi dari kelas abstrak dan ketika kita menerapkan kelas abstrak yang diwarisi mengapa kita harus menggunakan kata kunci override?

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

Karena metode ini dinyatakan abstrak di kelas induknya, itu tidak memiliki implementasi di kelas induk, jadi mengapa kata kunci menimpa diperlukan di sini?

psj01
sumber
2
Metode abstrak secara implisit adalah metode virtual, per spesifikasi
Pavel Anikhouski
"Pengubah override diperlukan untuk memperluas atau memodifikasi implementasi abstrak atau virtual dari metode, properti, pengindeks, atau peristiwa yang diwariskan." docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
gunr2171
2
Karena jika Anda tidak meletakkannya di sana, Anda "menyembunyikan" metodenya daripada menimpanya. Itulah sebabnya Anda mendapat peringatan bahwa "jika itu yang Anda maksudkan, gunakan kata newkunci` ... Anda juga akan mendapatkan kesalahan" tidak ada metode yang menggantikan metode kelas dasar ".
Ron Beyer
@RonBeyer dengan metode virtual ya, tetapi dengan abstrak itu tidak akan dikompilasi.
Johnathan Barclay

Jawaban:

14

Ketika kita membuat kelas yang mewarisi dari kelas abstrak dan ketika kita menerapkan kelas abstrak yang diwarisi mengapa kita harus menggunakan kata kunci override?

"Mengapa?" pertanyaan seperti ini bisa sulit dijawab karena tidak jelas. Saya akan berasumsi bahwa pertanyaan Anda adalah "argumen apa yang dapat dibuat selama desain bahasa untuk menyatakan posisi yang diperlukanoverride kata kunci ?"

Mari kita mulai dengan mengambil langkah mundur. Dalam beberapa bahasa, katakanlah, Java, metode adalah virtual secara default dan diganti secara otomatis. Para perancang C # menyadari hal ini dan menganggapnya sebagai cacat kecil di Jawa. C # bukan "Java dengan bagian-bagian bodoh dihilangkan" seperti yang dikatakan beberapa orang, tetapi para perancang C # ingin belajar dari titik-titik desain C, C ++ dan Java yang bermasalah, dan tidak mereplikasi mereka dalam C #.

Desainer C # dianggap sebagai sumber bug utama; Lagi pula, ini adalah cara untuk mengubah perilaku kode yang sudah ada dan diuji , dan itu berbahaya. Mengesampingkan bukanlah sesuatu yang harus dilakukan dengan santai atau kebetulan; itu harus dirancang oleh seseorang yang berpikir keras tentangnya . Itu sebabnya metode tidak virtual secara default, dan mengapa Anda diminta untuk mengatakan bahwa Anda mengganti metode.

Itulah alasan dasarnya. Kita sekarang bisa masuk ke beberapa alasan yang lebih maju.

Jawaban StriplingWarrior memberikan potongan pertama yang baik untuk membuat argumen yang lebih maju. Penulis kelas turunan mungkin tidak diberi tahu tentang kelas dasar, mungkin berniat untuk membuat metode baru, dan kami tidak boleh mengizinkan pengguna untuk menimpa secara tidak sengaja .

Meskipun poin ini masuk akal, ada sejumlah tandingan, seperti:

  • Penulis kelas turunan memiliki tanggung jawab untuk mengetahui segala sesuatu tentang kelas dasar! Mereka menggunakan kembali kode itu, dan mereka harus melakukan uji tuntas untuk memahami kode itu secara menyeluruh sebelum menggunakannya kembali.
  • Dalam skenario khusus Anda metode virtual adalah abstrak; itu akan menjadi kesalahan untuk tidak menimpanya, dan karena itu tidak mungkin bahwa penulis akan membuat implementasi secara tidak sengaja.

Mari kita buat argumen yang lebih maju tentang hal ini. Dalam keadaan apa penulis kelas turunan dapat dimaafkan karena tidak mengetahui apa yang dilakukan kelas dasar? Nah, pertimbangkan skenario ini:

  • Penulis kelas dasar membuat kelas dasar abstrak B.
  • Penulis kelas turunan, di tim yang berbeda, membuat kelas D turunan dengan metode M.
  • Penulis kelas dasar menyadari bahwa tim yang memperluas kelas dasar B akan selalu perlu menyediakan metode M, sehingga penulis kelas dasar menambahkan metode abstrak M.
  • Ketika kelas D dikompilasi ulang, apa yang terjadi?

Yang kami inginkan adalah penulis D diberi tahu bahwa sesuatu yang relevan telah berubah . Hal yang relevan yang telah berubah adalah bahwa M sekarang merupakan persyaratan dan bahwa implementasinya harus kelebihan beban. DM mungkin perlu mengubah perilakunya setelah kita tahu bahwa itu bisa dipanggil dari kelas dasar. Hal yang benar untuk dilakukan adalah tidak diam-diam mengatakan "oh, DM ada dan memperluas BM". Hal yang benar untuk dilakukan oleh kompiler adalah gagal , dan katakan "hai, penulis D, lihat asumsi Anda yang tidak lagi valid dan perbaiki kode Anda jika perlu".

Dalam contoh Anda, anggap overridesaja opsional pada SayHellokarena itu menimpa metode abstrak. Ada dua kemungkinan: (1) pembuat kode bermaksud untuk menimpa metode abstrak, atau (2) metode utama diganti secara tidak sengaja karena orang lain mengubah kelas dasar, dan kode tersebut sekarang salah dalam beberapa cara yang halus. Kami tidak dapat membedakan kemungkinan ini jika overrideopsional .

Tetapi jika overrideyang diperlukan maka kita dapat membedakan tiga skenario. Jika ada kesalahan mungkin dalam kode maka overrideyang hilang . Jika sengaja utama kemudian overrideadalah hadir . Dan jika sengaja tidak mengesampingkan kemudian newadalah hadir . Desain C # memungkinkan kita untuk membuat perbedaan yang halus ini.

Ingat, pelaporan kesalahan kompiler memerlukan pembacaan pikiran pengembang ; kompiler harus menyimpulkan dari kode yang salah apa kode yang benar yang mungkin ada dalam pikiran penulis , dan memberikan kesalahan yang mengarahkan mereka ke arah yang benar. Semakin banyak petunjuk yang dapat membuat pengembang meninggalkan kode tentang apa yang mereka pikirkan, semakin baik pekerjaan yang dapat dilakukan oleh kompiler dalam melaporkan kesalahan dan karenanya semakin cepat Anda dapat menemukan dan memperbaiki bug Anda.

Tetapi lebih umum, C # dirancang untuk dunia di mana kode berubah . Banyak sekali fitur C # yang tampak "aneh" sebenarnya ada di sana karena mereka memberi tahu pengembang ketika asumsi yang dulu valid telah menjadi tidak valid karena kelas dasar berubah. Kelas bug ini disebut "kegagalan kelas dasar getas", dan C # memiliki sejumlah mitigasi yang menarik untuk kelas kegagalan ini.

Eric Lippert
sumber
4
Terima kasih telah menjelaskan. Saya selalu menghargai jawaban Anda, baik karena memiliki suara berwibawa dari seseorang "di dalam" dan karena Anda melakukan pekerjaan yang baik dalam menjelaskan berbagai hal dengan sederhana dan sepenuhnya.
StriplingWarrior
Terima kasih atas penjelasan yang mendalam! sangat menghargai itu.
psj01
Poin bagus! Bisakah Anda membuat daftar beberapa mitigasi lain yang Anda sebutkan?
aksh1618
3

Itu untuk menentukan apakah Anda mencoba menimpa metode lain di kelas induk atau membuat implementasi baru yang unik untuk tingkat hirarki kelas ini. Bisa dibayangkan bahwa seorang programmer mungkin tidak menyadari keberadaan metode di kelas induk dengan tanda tangan yang sama persis seperti yang mereka buat di kelas mereka, yang dapat menyebabkan beberapa kejutan yang tidak menyenangkan.

Meskipun benar bahwa metode abstrak harus ditimpa dalam kelas anak yang tidak abstrak, perajin C # mungkin merasa masih lebih baik untuk eksplisit tentang apa yang Anda coba lakukan.

StriplingWarrior
sumber
Ini adalah awal yang baik untuk memahami alasan tim desain bahasa; Saya telah menambahkan jawaban yang menunjukkan bagaimana tim memulai dengan ide yang Anda ungkapkan di sini, tetapi melangkah lebih jauh.
Eric Lippert
1

Karena abstractmetode adalah metode virtual tanpa implementasi, per spesifikasi bahasa C # , berarti bahwa metode abstrak secara implisit adalah metode virtual. Dan overridedigunakan untuk memperluas atau memodifikasi implementasi abstrak atau virtual, seperti yang Anda lihat di sini

Untuk ulangi sedikit - Anda menggunakan metode virtual untuk mengimplementasikan beberapa jenis pengikatan akhir, sedangkan metode abstrak memaksa subkelas dari tipe tersebut agar metode tersebut diganti secara eksplisit. Itulah intinya, ketika metode itu virtual, itu bisa ditimpa, ketika itu adalah abstract- itu harus diganti

Pavel Anikhouski
sumber
1
Saya pikir pertanyaannya bukan mengenai apa yang dilakukannya, tetapi mengapa harus eksplisit.
Johnathan Barclay
0

Untuk menambahkan jawaban @ StriplingWarrior, saya pikir itu juga dilakukan untuk memiliki sintaks yang konsisten dengan mengesampingkan metode virtual di kelas dasar.

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

Jadi C # bisa dirancang sedemikian rupa sehingga Anda tidak perlu mengganti kata kunci untuk mengganti metode abstrak, tapi saya pikir para perancang memutuskan bahwa akan membingungkan karena tidak akan konsisten dengan mengganti metode virtual.

Polyfun
sumber
Re: "para desainer memutuskan bahwa akan membingungkan karena tidak akan konsisten dengan mengganti metode virtual" - ya, tetapi lebih. Misalkan Anda memiliki kelas dasar B dengan metode virtual M dan kelas turunan D dengan override. Sekarang anggaplah penulis B memutuskan untuk membuat M abstrak. Itu adalah perubahan besar, tapi mungkin mereka melakukannya. Pertanyaan: haruskah penulis D diminta untuk menghapus override? Saya pikir kebanyakan orang akan setuju bahwa tidak masuk akal untuk memaksa penulis D untuk membuat perubahan kode yang tidak perlu; kelas mereka baik-baik saja!
Eric Lippert