Apakah ada cara untuk mengganti metode non-virtual? atau sesuatu yang memberikan hasil yang serupa (selain membuat metode baru untuk memanggil metode yang diinginkan)?
Saya ingin mengganti metode dari Microsoft.Xna.Framework.Graphics.GraphicsDevice
dengan pengujian unit dalam pikiran.
c#
overriding
overloading
zfedoran.dll
sumber
sumber
Jawaban:
Tidak, Anda tidak dapat mengganti metode non-virtual. Hal terdekat yang dapat Anda lakukan adalah menyembunyikan metode dengan membuat
new
metode dengan nama yang sama tetapi ini tidak disarankan karena melanggar prinsip desain yang baik.Tetapi bahkan menyembunyikan metode tidak akan memberi Anda waktu eksekusi pengiriman polimorfik panggilan metode seperti yang dilakukan oleh panggilan metode virtual yang sebenarnya. Pertimbangkan contoh ini:
using System; class Example { static void Main() { Foo f = new Foo(); f.M(); Foo b = new Bar(); b.M(); } } class Foo { public void M() { Console.WriteLine("Foo.M"); } } class Bar : Foo { public new void M() { Console.WriteLine("Bar.M"); } }
Dalam contoh ini, kedua panggilan ke
M
metode cetakFoo.M
. Seperti yang Anda lihat, pendekatan ini memungkinkan Anda untuk memiliki implementasi baru untuk suatu metode selama referensi ke objek tersebut berasal dari jenis turunan yang benar, tetapi menyembunyikan metode dasar tidak merusak polimorfisme.Saya akan merekomendasikan agar Anda tidak menyembunyikan metode dasar dengan cara ini.
Saya cenderung berpihak pada mereka yang menyukai perilaku default C # yang metodenya non-virtual secara default (sebagai lawan dari Java). Saya akan melangkah lebih jauh dan mengatakan bahwa kelas juga harus disegel secara default. Warisan sulit untuk dirancang dengan benar dan fakta bahwa ada metode yang tidak ditandai sebagai virtual menunjukkan bahwa pembuat metode tersebut tidak pernah bermaksud agar metode tersebut diganti.
Edit: "waktu pelaksanaan pengiriman polimorfik" :
Yang saya maksud dengan ini adalah perilaku default yang terjadi pada waktu eksekusi saat Anda memanggil metode virtual. Katakanlah misalnya bahwa dalam contoh kode saya sebelumnya, daripada mendefinisikan metode non-virtual, saya sebenarnya mendefinisikan metode virtual dan metode overridden yang sebenarnya juga.
Jika saya harus memanggil
b.Foo
dalam kasus itu, CLR akan dengan benar menentukan jenis objek yangb
dirujuk referensi sebagaiBar
dan akan mengirimkan panggilan denganM
tepat.sumber
Tidak, kamu tidak bisa.
Anda hanya dapat mengganti metode virtual - lihat MSDN di sini :
sumber
Jika kelas dasar tidak disegel maka Anda dapat mewarisinya dan menulis metode baru yang menyembunyikan kelas dasar (gunakan kata kunci "baru" dalam deklarasi metode). Jika tidak, tidak, Anda tidak dapat menimpanya karena penulis asli tidak pernah bermaksud untuk menimpanya, oleh karena itu mengapa ini bukan virtual.
sumber
Saya pikir Anda mendapatkan overloading dan override bingung, overloading berarti Anda memiliki dua atau lebih metode dengan nama yang sama tetapi set parameter yang berbeda sementara overriding berarti Anda memiliki implementasi yang berbeda untuk metode dalam kelas turunan (dengan demikian mengganti atau memodifikasi perilaku di kelas dasarnya).
Jika sebuah metode virtual, Anda dapat menimpanya menggunakan kata kunci override di kelas derrived. Namun, metode non-virtual hanya dapat menyembunyikan implementasi dasar dengan menggunakan kata kunci baru sebagai pengganti kata kunci pengganti. Rute non-virtual tidak berguna jika pemanggil mengakses metode melalui variabel yang diketik sebagai tipe dasar karena kompilator akan menggunakan pengiriman statis ke metode dasar (artinya kode di kelas derrived Anda tidak akan pernah dipanggil).
Tidak pernah ada yang mencegah Anda untuk menambahkan beban berlebih ke kelas yang sudah ada, tetapi hanya kode yang mengetahui tentang kelas Anda yang dapat mengaksesnya.
sumber
Anda tidak dapat mengganti metode non-virtual kelas apa pun di C # (tanpa meretas CLR), tetapi Anda dapat mengganti metode antarmuka apa pun yang diterapkan kelas. Pertimbangkan kami memiliki non-disegel
class GraphicsDevice: IGraphicsDevice { public void DoWork() { Console.WriteLine("GraphicsDevice.DoWork()"); } } // with its interface interface IGraphicsDevice { void DoWork(); } // You can't just override DoWork in a child class, // but if you replace usage of GraphicsDevice to IGraphicsDevice, // then you can override this method (and, actually, the whole interface). class MyDevice: GraphicsDevice, IGraphicsDevice { public new void DoWork() { Console.WriteLine("MyDevice.DoWork()"); base.DoWork(); } }
Dan inilah demo
class Program { static void Main(string[] args) { IGraphicsDevice real = new GraphicsDevice(); var myObj = new MyDevice(); // demo that interface override works GraphicsDevice myCastedToBase = myObj; IGraphicsDevice my = myCastedToBase; // obvious Console.WriteLine("Using real GraphicsDevice:"); real.DoWork(); // override Console.WriteLine("Using overriden GraphicsDevice:"); my.DoWork(); } }
sumber
Jika Anda mewarisi dari kelas yang tidak diturunkan, Anda dapat dengan mudah membuat kelas super abstrak dan mewarisi darinya sebagai gantinya.
sumber
Anda tidak dapat mengganti metode non-virtual. Namun Anda dapat menggunakan
new
kata kunci pengubah untuk mendapatkan hasil yang serupa:class Class0 { public int Test() { return 0; } } class Class1 : Class0 { public new int Test() { return 1; } } . . . // result of 1 Console.WriteLine(new Class1().Test());
Anda juga akan ingin memastikan bahwa pengubah akses juga sama, jika tidak, Anda tidak akan mendapatkan warisan. Jika lain mewarisi kelas dari
Class1
yangnew
kata kunci dalamClass1
tidak akan mempengaruhi benda mewarisi dari itu, kecuali akses pengubah adalah sama.Jika pengubah akses tidak sama:
class Class0 { protected int Test() { return 0; } } class Class1 : Class0 { // different access modifier new int Test() { return 1; } } class Class2 : Class1 { public int Result() { return Test(); } } . . . // result of 0 Console.WriteLine(new Class2().Result());
... dibandingkan jika akses pengubah adalah sama:
class Class0 { protected int Test() { return 0; } } class Class1 : Class0 { // same access modifier protected new int Test() { return 1; } } class Class2 : Class1 { public int Result() { return Test(); } } . . . // result of 1 Console.WriteLine(new Class2().Result());
Seperti yang ditunjukkan dalam jawaban sebelumnya, ini bukanlah prinsip desain yang baik.
sumber
Ada cara untuk mencapai ini dengan menggunakan kelas abstrak dan metode abstrak.
Mempertimbangkan
Class Base { void MethodToBeTested() { ... } void Method1() { } void Method2() { } ... }
Sekarang, jika Anda ingin memiliki versi berbeda dari metode MethodToBeTested (), ubah Basis Kelas menjadi kelas abstrak dan metode MethodToBeTested () sebagai metode abstrak
abstract Class Base { abstract void MethodToBeTested(); void Method1() { } void Method2() { } ... }
Dengan void abstrak MethodToBeTested () muncul masalah; implementasinya hilang.
Oleh karena itu buatlah
class DefaultBaseImplementation : Base
agar memiliki implementasi default.Dan buat yang lain
class UnitTestImplementation : Base
untuk menerapkan pengujian unit.Dengan 2 kelas baru ini, fungsionalitas kelas dasar dapat diganti.
Class DefaultBaseImplementation : Base { override void MethodToBeTested() { //Base (default) implementation goes here } } Class UnitTestImplementation : Base { override void MethodToBeTested() { //Unit test implementation goes here } }
Sekarang Anda memiliki 2 kelas yang mengimplementasikan (mengganti)
MethodToBeTested()
.Anda dapat membuat instance kelas (turunan) sesuai kebutuhan (yaitu dengan implementasi dasar atau dengan implementasi pengujian unit).
sumber