Apa kegunaan praktis dari pengubah "baru" dalam C # sehubungan dengan bersembunyi?

21

Seorang rekan kerja dan saya melihat perilaku newkata kunci dalam C # yang berlaku untuk konsep persembunyian. Dari dokumentasi :

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

Kami telah membaca dokumentasinya, dan kami mengerti apa yang dasarnya dan bagaimana melakukannya. Apa yang tidak bisa kami tangani adalah mengapa Anda harus melakukannya sejak awal. Pengubah sudah ada sejak 2003, dan kami berdua telah bekerja dengan .Net lebih lama dari itu dan tidak pernah muncul.

Kapan perilaku ini diperlukan dalam arti praktis (misalnya: sebagaimana diterapkan pada kasus bisnis)? Apakah ini fitur yang telah melampaui kegunaannya atau apakah fungsinya cukup tidak biasa dalam apa yang kita lakukan (khususnya kita melakukan formulir web dan aplikasi MVC dan beberapa faktor kecil WinForms dan WPF)? Dalam mencoba kata kunci ini dan bermain dengannya kami menemukan beberapa perilaku yang memungkinkannya tampak sedikit berbahaya jika disalahgunakan.

Ini terdengar agak terbuka, tetapi kami sedang mencari kasus penggunaan tertentu yang dapat diterapkan ke aplikasi bisnis yang menganggap alat khusus ini bermanfaat.

Joel Etherton
sumber
9
Mungkin Anda pernah membacanya juga, tetapi ada artikel yang menarik dari Eric Lippert (pengembang kompiler C #) tentang mengapa metode persembunyian ditambahkan ke C #: blogs.msdn.com/b/ericlippert/archive/2008/05/21/… . Itu menjawab sebagian dari pertanyaan Anda, tetapi saya tidak punya kasing bisnis yang siap untuk Anda, jadi saya berikan komentar.
Jalayn
3
@Jalayn: Kasus bisnis dibahas oleh Eric dalam posting ini: blogs.msdn.com/b/ericlippert/archive/2004/01/07/…
Brian

Jawaban:

22

Anda dapat menggunakannya untuk meniru kovarians tipe kembali. Penjelasan Eric Lippert . Eric memberikan contoh kode ini:

abstract class Enclosure
{
    protected abstract Animal GetContents();
    public Animal Contents() { return this.GetContents(); }
}
class Aquarium : Enclosure
{
    public new Fish Contents() { ... }
    protected override Animal GetContents() { return this.Contents(); }
}

Ini adalah solusi. public override Fish Contents() { ... }tidak legal, meskipun aman.

Secara umum, Anda tidak boleh menggunakan metode persembunyian, karena membingungkan bagi konsumen kelas Anda (contoh spesifik di atas tidak menderita masalah ini). Beri nama metode baru Anda sesuatu yang lain jika Anda tidak ingin mengganti metode yang ada.

Kemungkinan situasi dunia nyata di mana Anda perlu menyembunyikan metode adalah jika penyedia kelas dasar menambahkan metode generik yang telah Anda tambahkan ke kelas turunan. Program seperti itu akan mengkompilasi (dan memberikan peringatan) tanpa kata kunci baru, tetapi menambahkan newmengatakan, "Saya tahu versi saya dari metode ini menggantikan versi kelas dasar. Ini mengerikan dan membingungkan, tetapi kami tidak dapat menggunakannya." Itu masih lebih baik daripada memaksa kelas turunan untuk mengganti nama metode mereka.

Membiarkan metode turunan diperlakukan sebagai override akan menyebabkan masalah. Mengabaikan masalah dengan penerapan kompiler, metode baru secara semantik berbeda dari metode dasar, tetapi polimorfisme akan menyebabkan metode baru dipanggil ketika diminta memanggil metode dengan nama yang sama.

Situasi ini dibahas secara rinci dalam posting ini oleh Eric Lippert.

Brian
sumber
1
+1 karena newmenjadi penanda untuk "ini mengerikan dan membingungkan".
Avner Shahar-Kashtan
Saya pikir contoh Eric sangat membantu, jika hanya karena saya berada dalam situasi yang sama ... Saya memiliki kelas dasar yang menerapkan metode nontrivial yang mengembalikan TBase, dan kelas turunan yang harus mengembalikan TDerived. Dalam hal ini, rasanya seperti kejahatan yang diperlukan.
Kyle Baran
4

Saya pikir itu ada jika Anda mungkin perlu melakukan sesuatu yang mungkin tidak terpikirkan oleh perancang bahasa. C # dalam banyak hal merupakan reaksi terhadap versi awal java. Dan satu hal yang dilakukan java adalah secara eksplisit pengembang pigeonhole untuk menghilangkan kemungkinan pengembang menembak diri mereka sendiri. C # mengambil pendekatan yang sedikit berbeda dan memberi pengembang sedikit lebih banyak kekuatan untuk memungkinkan pengembang kesempatan lebih banyak untuk menembak diri mereka sendiri. Salah satu contohnya adalah unsafekata kunci. newKata kunci ini adalah yang lain.

Sekarang, itu mungkin tidak berguna seperti unsafetetapi setelah Anda masuk ke spesifikasi bahasa sulit untuk keluar dari spesifikasi bahasa.

Wyatt Barnett
sumber
5
Java tidak memiliki yang baru karena Java memperlakukan semua metode sebagai virtual. Menurut Eric Lippert, motivasi untuk mendukung yang baru adalah untuk memecahkan masalah kelas dasar yang rapuh. Keberadaan baru diperlukan untuk penggunaan bisnis dunia nyata, tidak hanya untuk penggunaan perpustakaan tingkat rendah. Java tidak memiliki yang baru (dan tidak memiliki metode non-virtual) berarti bahwa jika kelas dasar memperkenalkan metode baru yang sudah digunakan dalam kelas turunan, kode yang ada dapat rusak, terlepas dari kenyataan bahwa pengembang kelas dasar harus diizinkan. tidak menyadari kode yang mengkonsumsinya.
Brian
3

Ia memberi tahu pembaca bahwa "Saya sengaja menyembunyikan penerapan metode ini oleh kelas dasar", dan bukannya tanpa sengaja.

Vegio
sumber
5
Ini menurut saya sebagai pertanyaan. OP tahu apa yang dilakukannya tetapi ingin tahu mengapa.
Brian
0

Anda mungkin ingin memiliki anggota sebelumnya tersedia melalui nama lain:

class VehicleClass
{
  public int AnyProperty
  {
    get; set;
  }

  public int AnyFunction() { return 0; }
} // VehicleClass

class IntermediateClass : VehicleClass
{
  public int PreviousAnyProperty
  {
    get { return AnyProperty; }
    set { AnyProperty = value  }
  }

  public int PreviousAnyFunction() { return AnyFunction(); }
} // IntermediateClass 

class CarClass : IntermediateClass
{
  public new int AnyProperty
  {
    get ; set ;
  }

  public new int AnyFunction() { return 5; }
} // class CarClass

class ExampleClass
{

  public static void Main()
  {
    using (CarClass MyCar = new CarClass())
    {
      int AnyInt1 = MyCar.PreviousAnyProperty;
      MyCar.PreviousAnyProperty = 7;

      int AnyInt2 = MyCar.PreviousAnyFunction();

      int AnyInt3 = MyCar.AnyProperty;
      MyCar.AnyProperty = 45;

      int AnyInt4 = MyCar.AnyFunction();
    }
  } // static void Main()

} // class CarClass

Tepuk tangan.

umlcat
sumber
Saya akrab dengan cara kerjanya, tetapi pertanyaan saya sebenarnya adalah Mengapa Anda ingin memiliki anggota sebelumnya tersedia melalui nama lain? Ini mematahkan semua jenis kontrak, menjadikan potongan generik tertentu tidak berguna.
Joel Etherton
@ Joel Etherton: Seperti yang mungkin Anda ketahui, terkadang pengembang harus "memilih" kode pemrograman orang lain. Dan kami mungkin tidak berwenang untuk memodifikasi, mungkin memperpanjang kelas. Dan mungkin perlu menggunakan keduanya, anggota sebelumnya & yang baru.
umlcat
0

Saya merasa cukup berguna ketika membuat suite uji untuk kode lawas. Itu memungkinkan saya untuk menyembunyikan dependensi eksternal, seperti menanyakan database di dalam metode yang digunakan oleh metode lain. Untuk dapat menguji metode lain saya bisa menyembunyikan logika asli yang tergantung pada sumber daya eksternal tanpa perlu menambahkan kata kunci virtual ke metode-metode di kelas uji.

Spacy
sumber