Perbedaan antara yang baru dan yang menimpa


198

Ingin tahu apa perbedaannya antara yang berikut:

Kasus 1: Kelas Dasar

public void DoIt();

Kasus 1: Kelas turunan

public new void DoIt();

Kasus 2: Kelas Dasar

public virtual void DoIt();

Kasus 2: Kelas turunan

public override void DoIt();

Baik case 1 dan 2 tampaknya memiliki efek yang sama berdasarkan tes yang saya jalankan. Apakah ada perbedaan, atau cara yang disukai?


2
Duplikat banyak pertanyaan, termasuk stackoverflow.com/questions/159978/…
Jon Skeet

Jawaban:


267

Pengubah override dapat digunakan pada metode virtual dan harus digunakan pada metode abstrak. Ini menunjukkan bagi kompiler untuk menggunakan implementasi metode yang didefinisikan terakhir. Bahkan jika metode ini dipanggil pada referensi ke kelas dasar itu akan menggunakan implementasi menimpanya.

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public override void DoIt()
    {
    }
}

Base b = new Derived();
b.DoIt();                      // Calls Derived.DoIt

akan memanggil Derived.DoItjika itu menimpa Base.DoIt.

Pengubah baru memerintahkan kompiler untuk menggunakan implementasi kelas anak Anda alih-alih implementasi kelas induk. Kode apa pun yang tidak mereferensikan kelas Anda tetapi kelas induk akan menggunakan implementasi kelas induk.

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public new void DoIt()
    {
    }
}

Base b = new Derived();
Derived d = new Derived();

b.DoIt();                      // Calls Base.DoIt
d.DoIt();                      // Calls Derived.DoIt

Akan lebih dulu menelepon Base.DoIt, laluDerived.DoIt . Mereka secara efektif dua metode yang sepenuhnya terpisah yang kebetulan memiliki nama yang sama, bukan metode turunan yang menggantikan metode dasar.

Sumber: blog Microsoft


5
This indicates for the compiler to use the last defined implementation of a method. bagaimana bisa menemukan implementasi metode yang didefinisikan terakhir ??
AminM

5
Mulai dari kelas yang konkret, periksa apakah ia memiliki implementasi metode yang menarik. Jika ya, berarti Anda sudah selesai. Jika tidak, naik satu langkah dalam hierarki warisan, yaitu, periksa apakah kelas super memiliki metode yang menarik. Lanjutkan sampai Anda menemukan metode yang menarik.
csoltenborn

2
Perhatikan juga bahwa Anda hanya dapat overridesebuah metode ketika kelas dasar mendefinisikan metode sebagai virtual. Kata itu virtualadalah kelas dasar yang mengatakan "Hei, ketika saya memanggil metode ini, sebenarnya bisa saja digantikan oleh implementasi yang diturunkan, jadi saya tidak benar-benar tahu sebelumnya apa implementasi metode yang sebenarnya saya panggil saat runtime. Jadi virtualmenandakan adalah pengganti untuk metode. Ini menyiratkan bahwa metode yang tidak ditandai sebagai virtualtidak dapat diganti. Tetapi Anda dapat mengganti metode non-virtual di kelas turunan dengan pengubah new, hanya dapat diakses di tingkat turunan
Erik Bongers

177

virtual : menunjukkan bahwa suatu metode dapat ditimpa oleh pewaris

override : menimpa fungsionalitas metode virtual di kelas dasar, menyediakan fungsionalitas yang berbeda.

baru : menyembunyikan metode asli (yang tidak harus virtual), menyediakan fungsionalitas yang berbeda. Ini hanya boleh digunakan di tempat yang mutlak diperlukan.

Ketika Anda menyembunyikan metode, Anda masih dapat mengakses metode asli dengan melemparkan ke kelas dasar. Ini berguna dalam beberapa skenario, tetapi berbahaya.


2
Mengapa casting metode yang menyembunyikan metode basis berbahaya? Atau apakah Anda menyiratkan bahwa casting secara umum berbahaya?
Markus

3
@ Mark - penelepon mungkin tidak mengetahui implementasi, menyebabkan penyalahgunaan yang tidak disengaja.
Jon B

Bisakah Anda menggunakan overridedan / atau newtanpa virtualmetode induk?
Aaron Franke

16

Dalam kasus pertama Anda menyembunyikan definisi di kelas induk. Ini berarti bahwa itu hanya akan dipanggil ketika Anda berurusan dengan objek sebagai kelas anak. Jika Anda melemparkan kelas ke tipe induknya, metode induk akan dipanggil. Dalam contoh kedua, metode ini diganti dan akan dipanggil terlepas dari apakah objek dilemparkan sebagai kelas anak atau orang tua.


7

coba yang berikut ini: (case1)

((BaseClass)(new InheritedClass())).DoIt()

Sunting: virtual + override diselesaikan pada saat runtime (jadi override benar-benar menimpa metode virtual), sementara yang baru buat metode baru dengan nama yang sama, dan sembunyikan yang lama, diselesaikan pada waktu kompilasi -> kompiler Anda akan memanggil metode itu ' melihat '


3

Dalam kasus 1 jika Anda menggunakan panggilan metode DoIt () dari kelas yang diwarisi sementara jenisnya dinyatakan sebagai kelas dasar Anda akan melihat aksi kelas dasar bahkan.

/* Results
Class1
Base1
Class2
Class2
*/
public abstract class Base1
{
    public void DoIt() { Console.WriteLine("Base1"); }
}
public  class Class1 : Base1 
{
    public new void DoIt() { Console.WriteLine("Class1"); }
}
public abstract class Base2
{
    public virtual void DoIt() { Console.WriteLine("Base2"); }
}
public class Class2 : Base2
{
    public override void DoIt() { Console.WriteLine("Class2"); }
}
static void Main(string[] args)
{
    var c1 = new Class1();
    c1.DoIt();
    ((Base1)c1).DoIt();

    var c2 = new Class2();
    c2.DoIt();
    ((Base2)c2).DoIt();
    Console.Read();
}

Bisakah Anda memposting peringatan atau kesalahan yang Anda terima. Kode ini berfungsi dengan baik ketika saya mempostingnya.
Matthew Whited

Ini semua harus disisipkan dalam kelas titik masuk Anda (Program). Itu telah dihapus untuk memungkinkan pemformatan yang lebih baik di situs ini.
Matthew Whited

3

Perbedaan antara dua kasus adalah bahwa dalam kasus 1, DoItmetode dasar tidak diganti, hanya disembunyikan. Ini artinya tergantung pada jenis variabel, tergantung pada metode mana yang akan dipanggil. Sebagai contoh:

BaseClass instance1 = new SubClass();
instance1.DoIt(); // Calls base class DoIt method

SubClass instance2 = new SubClass();
instance2.DoIt(); // Calls sub class DoIt method

Ini bisa sangat membingungkan dan menghasilkan perilaku yang tidak diharapkan dan harus dihindari jika memungkinkan. Jadi cara yang disukai adalah kasus 2.


3
  • newberarti menghargai tipe REFERENSI Anda (sisi kiri =), dengan demikian menjalankan metode tipe referensi. Jika metode yang didefinisi ulang tidak memiliki newkata kunci, ia berlaku seperti itu. Selain itu, itu juga dikenal sebagai warisan non-polimorfik . Yaitu, "Saya membuat metode baru di kelas turunan yang sama sekali tidak ada hubungannya dengan metode apa pun dengan nama yang sama di kelas dasar." - oleh kata Whitaker
  • override, yang harus digunakan dengan virtualkata kunci di kelas dasarnya, berarti menghormati tipe OBJECT Anda (sisi kanan =), dengan demikian menjalankan metode yang ditimpa terlepas dari jenis referensi. Selain itu, itu juga dikenal sebagai warisan polimorfik .

Cara saya mengingat kedua kata kunci itu saling bertentangan.

override: virtualkata kunci harus didefinisikan untuk mengganti metode. Metode yang menggunakan overridekata kunci yang terlepas dari jenis referensi (referensi dari kelas dasar atau kelas turunan) jika itu dipakai dengan kelas dasar, metode kelas dasar berjalan. Kalau tidak, metode kelas turunan berjalan.

new: jika kata kunci digunakan oleh suatu metode, tidak seperti overridekata kunci, jenis referensi itu penting. Jika itu dipakai dengan kelas turunan dan tipe referensi adalah kelas dasar, metode kelas dasar berjalan. Jika dipakai dengan kelas turunan dan tipe referensi adalah kelas turunan, metode kelas turunan berjalan. Yaitu, ini kontras dengan overridekata kunci. En passant, jika Anda lupa atau menghilangkan untuk menambahkan kata kunci baru ke metode, kompiler berperilaku secara default saat newkata kunci digunakan.

class A 
{
    public string Foo() 
    {
        return "A";
    }

    public virtual string Test()
    {
        return "base test";
    }
}

class B: A
{
    public new string Foo() 
    {
        return "B";
    }
}

class C: B 
{
    public string Foo() 
    {
        return "C";
    }

    public override string Test() {
        return "derived test";
    }
}

Panggilan utama:

A AClass = new B();
Console.WriteLine(AClass.Foo());
B BClass = new B();
Console.WriteLine(BClass.Foo());
B BClassWithC = new C();
Console.WriteLine(BClassWithC.Foo());

Console.WriteLine(AClass.Test());
Console.WriteLine(BClassWithC.Test());

Keluaran:

A
B
B
base test
derived test

Contoh kode baru,

Main dengan kode dengan berkomentar satu per satu.

class X
{
    protected internal /*virtual*/ void Method()
    {
        WriteLine("X");
    }
}
class Y : X
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Y");
    }
}
class Z : Y
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Z");
    }
}

class Programxyz
{
    private static void Main(string[] args)
    {
        X v = new Z();
        //Y v = new Z();
        //Z v = new Z();
        v.Method();
}

1

Jika kata kunci override digunakan dalam turunan kelas maka menimpa metode induknya.

Jika Kata Kunci newdigunakan dalam turunan kelas maka turunkan metode disembunyikan oleh metode induk.


1

Saya memiliki pertanyaan yang sama dan ini benar-benar membingungkan, Anda harus mempertimbangkan bahwa mengganti dan kata kunci baru hanya berfungsi dengan objek dari kelas tipe dasar dan nilai kelas turunan. Dalam hal ini hanya Anda yang akan melihat efek override dan baru: Jadi jika Anda memiliki class Adan B, Bmewarisi dari A, maka Anda instantiate objek seperti ini:

A a = new B();

Sekarang memanggil metode akan mempertimbangkan keadaannya. Override : berarti ia memperluas fungsi metode, kemudian menggunakan metode di kelas turunan, sedangkan yang baru memberitahu kompiler untuk menyembunyikan metode di kelas turunan dan menggunakan metode di kelas dasar sebagai gantinya. Ini adalah pemandangan yang sangat bagus untuk subjek itu:

https://msdn.microsoft.com/EN-US/library/ms173153%28v=VS.140,d=hv.2%29.aspx?f=255&MSPPError=-2147217396


1

Artikel di bawah ini ada di vb.net tapi saya pikir penjelasan tentang penggantian baru vs sangat mudah dipahami.

https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides

Di beberapa titik dalam artikel, ada kalimat ini:

Secara umum, Bayangan mengasumsikan fungsi yang terkait dengan tipe dipanggil, sementara Overrides menganggap implementasi objek dieksekusi.

Jawaban yang diterima untuk pertanyaan ini sempurna tetapi saya pikir artikel ini memberikan contoh yang baik untuk menambahkan makna yang lebih baik tentang perbedaan antara dua kata kunci ini.


1

Dari semua itu, baru adalah yang paling membingungkan. Dengan bereksperimen, kata kunci baru seperti memberi pengembang pilihan untuk menimpa implementasi kelas pewarisan dengan implementasi kelas dasar dengan secara eksplisit mendefinisikan jenisnya. Ini seperti berpikir sebaliknya.

Dalam contoh di bawah ini, hasilnya akan mengembalikan "Hasil turunan" hingga jenisnya secara eksplisit didefinisikan sebagai uji BaseClass, hanya kemudian "Hasil dasar" akan dikembalikan.

class Program
{
    static void Main(string[] args)
    {
        var test = new DerivedClass();
        var result = test.DoSomething();
    }
}

class BaseClass
{
    public virtual string DoSomething()
    {
        return "Base result";
    }
}

class DerivedClass : BaseClass
{
    public new string DoSomething()
    {
        return "Derived result";
    }
}

3
Tambahkan komentar Anda jika Anda keberatan. Tabrak lari sangat pengecut.
bermanfaatBee

0

Perbedaan fungsional tidak akan ditampilkan dalam tes ini:

BaseClass bc = new BaseClass();

bc.DoIt();

DerivedClass dc = new DerivedClass();

dc.ShowIt();

Dalam contoh ini, Doit yang dipanggil adalah yang Anda harapkan untuk dipanggil.

Untuk melihat perbedaannya, Anda harus melakukan ini:

BaseClass obj = new DerivedClass();

obj.DoIt();

Anda akan melihat apakah Anda menjalankan tes itu dalam kasus 1 (seperti yang Anda definisikan), DoIt()in BaseClassdisebut, dalam kasus 2 (seperti yang Anda definisikan), DoIt()in DerivedClassdisebut.


-1

Pada kasus pertama itu akan memanggil metode DoIt () kelas turunan karena kata kunci baru menyembunyikan metode DoIt () kelas dasar.

Pada kasus kedua ini akan memanggil DoIt () overriden ()

  public class A
{
    public virtual void DoIt()
    {
        Console.WriteLine("A::DoIt()");
    }
}

public class B : A
{
    new public void DoIt()
    {
        Console.WriteLine("B::DoIt()");
    }
}

public class C : A
{
    public override void DoIt()
    {
        Console.WriteLine("C::DoIt()");
    }
}

mari buat instance dari kelas-kelas ini

   A instanceA = new A();

    B instanceB = new B();
    C instanceC = new C();

    instanceA.DoIt(); //A::DoIt()
    instanceB.DoIt(); //B::DoIt()
    instanceC.DoIt(); //B::DoIt()

Semuanya diharapkan di atas. Biarkan atur instanceB dan instanceC ke instanceA dan panggil metode DoIt () dan periksa hasilnya.

    instanceA = instanceB;
    instanceA.DoIt(); //A::DoIt() calls DoIt method in class A

    instanceA = instanceC;
    instanceA.DoIt();//C::DoIt() calls DoIt method in class C because it was overriden in class C

instanceC.DoIt (); akan memberi Anda C :: DoIt (), bukan B :: DoIt ()
BYS2

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.