kata kunci baru dalam tanda tangan metode

113

Saat melakukan refactoring, saya akhirnya membuat metode seperti contoh di bawah ini. Jenis data telah diubah demi kesederhanaan.

Saya sebelumnya memiliki pernyataan tugas seperti ini:

MyObject myVar = new MyObject();

Itu direfraktorisasi menjadi ini secara tidak sengaja:

private static new MyObject CreateSomething()
{
  return new MyObject{"Something New"};
}

Ini adalah hasil dari kesalahan potong / tempel di pihak saya, tetapi newkata kunci dalam private static newvalid dan dapat dikompilasi.

Pertanyaan : Apa arti newkata kunci dalam tanda tangan metode? Saya berasumsi itu adalah sesuatu yang diperkenalkan di C # 3.0?

Apa bedanya dengan ini override?

p. campbell
sumber
5
Beberapa catatan tentang keinginan menyembunyikan metode: blogs.msdn.com/ericlippert/archive/2008/05/21/…
Eric Lippert
2
@El .. Pos yang bagus. Saya tidak pernah berpikir metode bersembunyi seperti itu, karena ya, objek ADALAH satu hal, tetapi kami sekarang menyajikannya sebagai sesuatu yang lain, jadi kami ingin perilaku dari hal yang kami tampilkan itu AS. Pintar ...
BFree
1
Pertanyaan duplikat di masa mendatang dan saya sudah mencoba menjawab dengan beberapa detail: Gunakan kata kunci baru di c #
Ken Kin
1
Penggunaan newsebagai pengubah pada metode (atau anggota tipe lain) bukanlah "sesuatu yang diperkenalkan di C # 3.0". Itu sudah ada sejak versi pertama C #.
Jeppe Stig Nielsen
1
Ada 4 duplikat pertanyaan ini di SO. Menurut pendapat saya, yang satu ini akan memberi Anda pemahaman terbaik: stackoverflow.com/questions/3117838/… . Itu dijawab oleh @EricLippert.
Raikol Amaro

Jawaban:

101

Referensi kata kunci baru dari MSDN:

Referensi MSDN

Berikut adalah contoh yang saya temukan di internet dari Microsoft MVP yang masuk akal: Tautan ke Asli

public class A
{
   public virtual void One();
   public void Two();
}

public class B : A
{
   public override void One();
   public new void Two();
}

B b = new B();
A a = b as A;

a.One(); // Calls implementation in B
a.Two(); // Calls implementation in A
b.One(); // Calls implementation in B
b.Two(); // Calls implementation in B

Penimpaan hanya dapat digunakan dalam kasus yang sangat spesifik. Dari MSDN:

Anda tidak dapat mengganti metode non-virtual atau statis. Metode dasar yang diganti harus virtual, abstrak, atau timpa.

Jadi kata kunci 'baru' diperlukan untuk memungkinkan Anda 'menimpa' metode non-virtual dan statis.

Kelsey
sumber
4
saya baru saja menjalankan sampel Anda .. itu akan menimpa meskipun Anda tidak menentukan "baru", bukan?
Michael Sync
2
@MichaelSync tepatnya, jadi mengapa kita perlu menyebutkan kata kunci baru?
ZoomIn
2
"Meskipun Anda dapat menyembunyikan anggota tanpa menggunakan pengubah baru, Anda mendapatkan peringatan compiler." per dokumen tertaut. Jadi, kata kunci menjelaskan bahwa Anda bermaksud bersembunyi, dan peringatan tidak dikeluarkan.
Jim Menghitung
1
-1 -> tidak diperlukan sama sekali - perilakunya akan sama. Sangat membingungkan bagi seorang pemula tentang apa newitu, tetapi saya pikir itu benar-benar hanya ada untuk dibaca - kompiler hanya memperingatkan Anda bahwa ketika Anda memanggil metode kelas turunan dengan nama yang sama sebagai basis, Anda tidak akan mendapatkan metode kelas dasar yang mungkin Anda lakukan. pikir .... itu aneh ... murni untuk dibaca?
Don Cheadle
1
Demi kelengkapan: Jika kelas A dan B mengimplementasikan antarmuka IMyInterface, implementasi pada kelas Derived akan dipanggil. Dengan demikian IMyInterface c = new B()akan memanggil implementasi kelas B. Jika hanya satu kelas yang mengimplementasikan antarmuka, metode dari kelas yang mengimplementasikannya akan dipanggil.
Nullius
61

Tidak, ini sebenarnya bukan "baru" (maafkan permainan kata). Ini pada dasarnya digunakan untuk "menyembunyikan" metode. YAITU:

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

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

Jika Anda kemudian melakukan ini:

Base b = new Derived();
b.Method();

Metode di Basis adalah salah satu yang akan dipanggil, BUKAN yang diturunkan.

Beberapa info lebih lanjut: http://www.akadia.com/services/dotnet_polymorphism.html

Re-edit Anda: Dalam contoh yang saya berikan, jika Anda "menimpa" daripada menggunakan "baru" maka saat Anda memanggil b.Method (); Metode kelas Turunan akan disebut karena Polimorfisme.

BFree
sumber
Kelas publik Basis {Metode kekosongan virtual publik () {Console.WriteLine ("Base"); }} public class Berasal: Base {public void Method () {Console.WriteLine ("Derived"); }} Base b = new Derived (); b. Metode (); Saya mendapat "Basis" .. Jika saya menambahkan "baru" di kelas Turunan, saya masih mendapatkan "Basis" ..
Michael Sync
@ michael metode ini masih virtual
Rune FS
@MichaelSync: Anda seharusnya melihat peringatan dengan kode itu; Peringatan Derived.Method () 'menyembunyikan anggota' Base.Method () 'yang diwariskan. Untuk membuat anggota saat ini mengganti implementasi tersebut, tambahkan kata kunci override. Jika tidak, tambahkan kata kunci baru.
Christopher McAtackney
2
@MichaelSync Jika kata "override" ditinggalkan, maka perilaku default-nya adalah "baru" misalnya penyembunyian metode. Jadi fakta bahwa Anda meninggalkan kata baru tidak ada bedanya.
BFree
1
Iya. Itulah yang saya pikirkan juga. Tidak yakin mengapa C # menambahkan kata kunci "baru" .. ini hanya untuk membuat peringatan menghilang .. stackoverflow.com/questions/8502661/…
Michael Sync
22

Seperti yang dijelaskan orang lain, ini digunakan untuk menyembunyikan metode yang sudah ada. Ini berguna untuk mengganti metode yang bukan virtual di kelas induk.

Ingatlah bahwa membuat anggota "baru" tidak bersifat polimorfik. Jika Anda mentransmisikan objek ke tipe dasar, itu tidak akan menggunakan anggota tipe turunan.

Jika Anda memiliki kelas dasar:

public class BaseClass
{
    public void DoSomething() { }
}

Dan kemudian kelas turunannya:

public class DerivedType : BaseClass
{
    public new void DoSomething() {}

}

Jika Anda mendeklarasikan sebuah tipe DerivedTypedan kemudian mentransmisikannya, metode DoSomething()tersebut bukan polimorfik, metode ini akan memanggil metode kelas dasar, bukan yang diturunkan.

BaseClass t = new DerivedType();
t.DoSomething();// Calls the "DoSomething()" method of the base class.
Dan Herbert
sumber
1
Ini akan memanggil metode dari kelas dasar meskipun Anda menghapus "baru" dari DerievedType juga ..
Michael Sync
2
Saya pikir paragraf kedua Anda memenuhi seluruh topik ini. Saat menangani panggilan dari referensi kelas dasar , "baru" bukanlah polimorfik ... yaitu. Anda mendapatkan apa yang Anda tentukan saat melakukan panggilan. "override" bersifat polimorfik ... yaitu. Anda mendapatkan apa yang ditentukan oleh hierarki kelas .
Jonathon Reinhart
bagaimana ini bisa didefinisikan secara samar-samar? Sangat membuat frustasi bagi seorang pemula. Dan tidak, itu tidak ada hubungannya dengan polimorfisme - ini memiliki perilaku yang sama persis seperti tidak memiliki overridetanda tangan dalam metode
Don Cheadle
Apa yang tersembunyi dari apa? Tentunya tidak mungkin newmenyembunyikan tipe dasar dari tipe turunan, karena tidak bisakah Anda selalu mengakses tipe dasar menggunakan base.<type>notasi?
thatWiseGuy
@thatWiseGuy Ini "tersembunyi" dalam arti bahwa metode dasar tidak akan dipanggil saat menggunakan kelas turunan dalam kode Anda. Ini masih bisa dipanggil secara internal base.<method>, dan juga bisa dipanggil secara eksternal jika Anda mentransmisikan ke tipe dasar dalam kode Anda. Lebih akurat secara teknis untuk menganggapnya sebagai "mengesampingkan" metode dasar daripada "menyembunyikannya".
Dan Herbert
7

Dari dokumen:

Jika metode di kelas turunan diawali dengan kata kunci baru, metode tersebut didefinisikan sebagai metode independen di kelas dasar.

Artinya dalam praktik:

Jika Anda mewarisi dari kelas lain dan Anda memiliki metode yang berbagi tanda tangan yang sama, Anda dapat mendefinisikannya sebagai 'baru' sehingga independen dari kelas induk. Artinya jika Anda memiliki referensi ke kelas 'induk' maka implementasi tersebut akan dijalankan, jika Anda memiliki referensi ke kelas anak maka implementasi tersebut akan dijalankan.

Secara pribadi saya mencoba untuk menghindari kata kunci 'baru' karena biasanya berarti saya memiliki hierarki kelas yang salah, tetapi ada kalanya hal itu dapat berguna. Satu tempat untuk versi dan kompatibilitas mundur.

Ada banyak informasi di MSDN untuk ini .

jonnii
sumber
Fakta bahwa perilaku ini terjadi tidak ada hubungannya dengan newkeberadaannya. Ini sintaksis / keterbacaan.
Don Cheadle
3

Artinya metode tersebut menggantikan metode dengan nama yang sama yang diwarisi oleh kelas dasar. Dalam kasus Anda, Anda mungkin tidak memiliki metode dengan nama itu di kelas dasar, yang berarti kata kunci baru sama sekali tidak berguna.

Robin Clowers
sumber
3

Singkat cerita - TIDAK diperlukan, TIDAK mengubah perilaku, dan MURNI ada untuk dibaca.

Itulah mengapa di VS Anda akan melihat sedikit berlekuk-lekuk, namun kode Anda akan dikompilasi dan berjalan dengan baik dan seperti yang diharapkan.

Seseorang harus bertanya-tanya apakah itu benar-benar layak membuat newkata kunci ketika semua artinya adalah pengembang yang mengakui "Ya, saya tahu saya menyembunyikan metode dasar, ya saya tahu saya tidak melakukan apa pun yang berhubungan dengan virtualatau overriden(polimorfisme) - Saya benar-benar ingin membuat metodenya sendiri ".

Agak aneh bagi saya, tetapi mungkin hanya karena saya berasal dari Javalatar belakang dan ada perbedaan mendasar antara C#inheritance dan Java: Dalam Java, metode virtual secara default kecuali ditentukan oleh final. Dalam C#, metode bersifat final / konkret secara default kecuali ditentukan oleh virtual.

Don Cheadle
sumber
1

Dari MSDN :

Gunakan pengubah baru untuk secara eksplisit menyembunyikan anggota yang diwarisi dari kelas dasar. Untuk menyembunyikan anggota yang diwariskan, deklarasikannya di kelas turunan menggunakan nama yang sama, dan modifikasi dengan pengubah baru.

Doug R
sumber
0

Hati-hati dengan gotcha ini.
Anda memiliki metode yang ditentukan dalam antarmuka yang diimplementasikan di kelas dasar. Anda kemudian membuat kelas turunan yang menyembunyikan metode antarmuka, tetapi tidak secara khusus mendeklarasikan kelas turunan sebagai mengimplementasikan antarmuka. Jika Anda kemudian memanggil metode melalui referensi ke antarmuka, metode kelas dasar akan dipanggil. Namun jika kelas turunan Anda secara khusus mengimplementasikan antarmuka, maka metodenya akan dipanggil jenis referensi mana pun yang digunakan.

interface IMethodToHide
{
    string MethodToHide();
}

class BaseWithMethodToHide : IMethodToHide
{
    public string MethodToHide()
    {
        return "BaseWithMethodToHide";
    }
}

class DerivedNotImplementingInterface   : BaseWithMethodToHide
{
    new public string MethodToHide()
    {
        return "DerivedNotImplementingInterface";
    }
}

class DerivedImplementingInterface : BaseWithMethodToHide, IMethodToHide
{
    new public string MethodToHide()
    {
        return "DerivedImplementingInterface";
    }
}

class Program
{
    static void Main()
    {
        var oNoI = new DerivedNotImplementingInterface();
        IMethodToHide ioNoI = new DerivedNotImplementingInterface();

        Console.WriteLine("reference to the object type DerivedNotImplementingInterface calls the method in the class " 
            + oNoI.MethodToHide());
        // calls DerivedNotImplementingInterface.MethodToHide()
        Console.WriteLine("reference to a DerivedNotImplementingInterface object via the interfce IMethodToHide calls the method in the class " 
            + ioNoI.MethodToHide());
        // calls BaseWithMethodToHide.MethodToHide()
        Console.ReadLine();

        var oI = new DerivedImplementingInterface();
        IMethodToHide ioI = new DerivedImplementingInterface();

        Console.WriteLine("reference to the object type DerivedImplementingInterface calls the method in the class " 
            + oI.MethodToHide());
        // calls DerivedImplementingInterface.MethodToHide()
        Console.WriteLine("reference to a DerivedImplementingInterface object via the interfce IMethodToHide calls the method in the class " 
            + ioI.MethodToHide());
        // calls DerivedImplementingInterface.MethodToHide()
        Console.ReadLine();

    }
}
Mantel pinggang yang terlalu penuh
sumber