Mengapa metode ajaib diimplementasikan dalam C #?


16

Di C #, saya mulai melihat semua metode ajaib ini muncul, tanpa didukung oleh antarmuka. Mengapa ini dipilih?

Biarkan saya jelaskan.

Sebelumnya di C #, jika suatu objek mengimplementasikan IEnumerableantarmuka, itu akan secara otomatis menjadi iterable oleh satu foreachloop. Itu masuk akal bagi saya, karena didukung oleh antarmuka, dan jika saya memiliki Iteratorfungsi saya sendiri di dalam kelas yang diulangi, saya bisa melakukannya tanpa khawatir bahwa itu secara ajaib akan berarti sesuatu yang lain.

Sekarang, tampaknya, (tidak yakin kapan), antarmuka ini tidak lagi diperlukan. Itu hanya perlu memiliki konversi penamaan yang tepat.

Contoh lain adalah membuat objek apa pun ditunggu dengan memiliki metode yang dinamai persis GetAwaiter yang memiliki beberapa properti tertentu.

Mengapa tidak membuat antarmuka seperti sebelumnya IEnumerable atau INotifyPropertyChangedmendukung "sihir" ini secara statis?

Lebih detail tentang apa yang saya maksud di sini:

http://blog.nem.ec/2014/01/01/magic-methods-c-sharp/

Apa pro dan kontra dari metode sulap, dan adakah di mana pun secara online di mana saya dapat menemukan apa pun tentang mengapa keputusan ini dibuat?


4
Anda harus mengedit opini pribadi Anda mengapa "sihir itu buruk" jika Anda tidak ingin ditutup.
DougM

2
Jika Anda ingin tahu mengapa Anders Hejlsberg melakukan itu, Anda harus bertanya kepadanya. Saya hanya bisa memberi tahu Anda mengapa saya melakukan itu, dan itu untuk kompatibilitas ke depan. Metode ekstensi memungkinkan Anda untuk "memalsukan" menambahkan metode ke jenis yang ada, tetapi tidak ada antarmuka ekstensi. Jika Anda memerlukan antarmuka untuk, katakanlah, async/ await, maka itu hanya akan bekerja dengan kode yang ditulis setelah .NET 4.5 menjadi cukup luas untuk menjadi target yang layak ... yang pada dasarnya sekarang. Tetapi terjemahan sintaksis murni ke panggilan metode memungkinkan saya untuk menambahkan awaitfungsionalitas ke tipe yang ada setelah fakta.
Jörg W Mittag

2
Perhatikan bahwa ini pada dasarnya diterapkan orientasi objek: selama objek merespons pesan yang sesuai, itu dianggap jenis yang benar.
Jörg W Mittag

7
"Sebelumnya" dan "sekarang" Anda terbelakang - metode ajaib diimplementasikan sebagai fungsi dasar untuk foreachperulangan di awal. Tidak pernah ada persyaratan untuk objek untuk diterapkan IEnumerableuntuk foreachbekerja. Sudah konvensi untuk melakukannya.
Jesse C. Slicer

2
@gbulmer ini adalah di mana saya ingat mendapatkan ide dari: blogs.msdn.com/b/ericlippert/archive/2011/06/30/… (dan pada tingkat lebih rendah ericlippert.com/2013/07/22/… )
Jesse C. Slicer

Jawaban:


16

Secara umum "metode ajaib" digunakan ketika itu tidak mungkin untuk membuat antarmuka yang akan bekerja dengan cara yang sama.

Ketika foreachpertama kali diperkenalkan di C # 1.0 (perilaku itu tentu tidak baru), ia harus menggunakan metode sihir, karena tidak ada obat generik. Opsi pada dasarnya adalah:

  1. Gunakan non-generik IEnumerabledan IEnumeratorbekerja dengan objects, yang berarti tipe nilai tinju. Karena iterasi sesuatu seperti daftar ints harus sangat cepat dan tentunya tidak boleh membuat banyak nilai kotak sampah, ini bukan pilihan yang baik.

  2. Tunggu obat generik. Ini mungkin berarti menunda. Net 1.0 (atau setidaknya foreach) lebih dari 3 tahun.

  3. Gunakan metode ajaib.

Jadi, mereka memilih opsi # 3 dan tetap dengan kami untuk alasan kompatibilitas mundur, meskipun sejak. Net 2.0, membutuhkan IEnumerable<T>akan bekerja juga.


Penginisialisasi koleksi dapat terlihat berbeda pada setiap jenis koleksi. Bandingkan List<T>:

public void Add(T item)

dan Dictionary<TKey, TValue>:

public void Add(TKey key, TValue value)

Anda tidak dapat memiliki antarmuka tunggal yang hanya mendukung bentuk pertama List<T>dan hanya untuk kedua Dictionary<TKey, TValue>.


Metode LINQ biasanya diimplementasikan sebagai metode ekstensi (sehingga hanya ada satu implementasi misalnya LINQ ke Objek untuk semua jenis yang menerapkan IEnumerable<T>), yang berarti tidak mungkin menggunakan antarmuka.


Untuk await, GetResult()Metode dapat mengembalikan salah satu voidatau beberapa jenis T. Sekali lagi, Anda tidak dapat memiliki antarmuka tunggal yang dapat menangani keduanya. Meskipun awaitsebagian berbasis antarmuka: penunggu harus mengimplementasikan INotifyCompletiondan juga dapat mengimplementasikan ICriticalNotifyCompletion.


1
Apakah Anda yakin "mereka memilih opsi # 3" untuk C # 1.0? Ingatan saya adalah pilihan 1. Memori saya adalah, C # mengklaim keunggulan signifikan atas Java karena kompiler C # melakukan 'auto-boxing' dan 'auto-unboxing'. Dikatakan, C # membuat kode seperti itu foreach bekerja jauh lebih baik daripada Jawa sebagian karena itu.
gbulmer

1
Dokumentasi untuk foreachdi C # 1.2 (tidak ada pada MSDN untuk C # 1.0) mengatakan bahwa ekspresi dalam foreach"Mengevaluasi ke tipe yang mengimplementasikan IEnumerableatau tipe yang menyatakan GetEnumeratormetode." Dan saya tidak yakin apa yang Anda maksud dengan itu, unboxing di C # selalu eksplisit.
svick

1
@gbulmer Dan spesifikasi ECMA untuk C # dari Desember 2001 (yang harus berarti itu untuk C # 1.0) juga mengatakan bahwa (§15.8.4): “Tipe C dikatakan sebagai tipe koleksi jika mengimplementasikan antarmuka System.IEnumerable atau mengimplementasikan pola pengumpulan dengan memenuhi semua kriteria berikut [...] ”.
svick

1
@svick foreach(T x in sequence)menerapkan gips eksplisit Tpada elemen-elemen urutan. Jadi, jika urutannya polos IEnumerabledan Tmerupakan tipe nilai, ia akan menghapus kotaknya tanpa ada tulisan eksplisit yang ditulis dalam kode Anda. Salah satu bagian terburuk dari C #.
CodesInChaos

@CodesInChaos "Auto-unboxing" terdengar cukup umum, saya tidak menyadari itu merujuk fitur khusus ini. (Kurasa seharusnya begitu, karena kita sedang membicarakannya foreach.)
svick
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.