Untuk beberapa alasan saya menyelinap ke sumber .NET Framework untuk kelas Double
dan menemukan bahwa deklarasi ==
adalah:
public static bool operator ==(Double left, Double right) {
return left == right;
}
Logika yang sama berlaku untuk setiap operator.
- Apa gunanya definisi seperti itu?
- Bagaimana cara kerjanya?
- Mengapa itu tidak menciptakan rekursi yang tak terbatas?
c#
.net
language-lawyer
Thomas Ayoub
sumber
sumber
ceq
dikeluarkan di IL. Ini hanya di sana untuk mengisi beberapa tujuan dokumentasi, tetapi tidak dapat menemukan sumbernya.Jawaban:
Pada kenyataannya, kompiler akan mengubah
==
operator menjadiceq
kode IL, dan operator yang Anda sebutkan tidak akan dipanggil.Alasan untuk operator dalam kode sumber kemungkinan sehingga dapat dipanggil dari bahasa selain C # yang tidak menerjemahkannya menjadi
CEQ
panggilan langsung (atau melalui refleksi). Kode dalam operator akan dikompilasi menjadi aCEQ
, sehingga tidak ada rekursi yang tak terbatas.Bahkan, jika Anda memanggil operator melalui refleksi, Anda dapat melihat bahwa operator dipanggil (bukan
CEQ
instruksi), dan jelas tidak rekursif tanpa batas (karena program berakhir seperti yang diharapkan):IL yang dihasilkan (dikompilasi oleh LinqPad 4):
Menariknya - operator yang sama TIDAK ada (baik dalam sumber referensi atau melalui refleksi) untuk jenis yang tidak terpisahkan, hanya
Single
,Double
,Decimal
,String
, danDateTime
, yang menyangkal teori saya bahwa mereka ada untuk dipanggil dari bahasa lain. Jelas Anda dapat menyamakan dua bilangan bulat dalam bahasa lain tanpa operator ini, jadi kami kembali ke pertanyaan "mengapa mereka ada untukdouble
"?sumber
Kebingungan utama di sini adalah Anda berasumsi bahwa semua .NET libraries (dalam hal ini, Extended Numerics Library, yang bukan merupakan bagian dari BCL) ditulis dalam standar C #. Ini tidak selalu terjadi, dan berbagai bahasa memiliki aturan yang berbeda.
Dalam standar C #, potongan kode yang Anda lihat akan menghasilkan stack overflow, karena cara kerja resolusi kelebihan operator. Namun, kode tersebut sebenarnya tidak dalam standar C # - pada dasarnya menggunakan fitur tidak terdaftar dari kompiler C #. Alih-alih memanggil operator, ia memancarkan kode ini:
Itu saja :) Tidak ada 100% kode C # yang setara - ini tidak mungkin dilakukan dalam C # dengan tipe Anda sendiri .
Bahkan kemudian, operator yang sebenarnya tidak digunakan ketika mengkompilasi kode C # - kompiler melakukan banyak optimasi, seperti dalam kasus ini, di mana ia menggantikan
op_Equality
panggilan hanya dengan sederhanaceq
. Sekali lagi, Anda tidak dapat meniru ini diDoubleEx
struct Anda sendiri - itu adalah kompiler ajaib.Ini tentu saja bukan situasi yang unik di .NET - ada banyak kode yang tidak valid, standar C #. Alasannya biasanya (a) hack kompiler dan (b) bahasa yang berbeda, dengan hack runtime aneh (c) (Saya sedang melihat Anda
Nullable
,!).Karena kompiler Roslyn C # adalah sumber oepn, saya benar-benar dapat mengarahkan Anda ke tempat resolusi overload ditentukan:
Tempat di mana semua operator biner diselesaikan
"Pintasan" untuk operator intrinsik
Ketika Anda melihat pintasan, Anda akan melihat bahwa kesetaraan antara hasil ganda dan ganda di operator ganda intrinsik, tidak pernah di
==
operator yang sebenarnya ditentukan pada jenis. Sistem tipe .NET harus berpura-pura bahwa ituDouble
adalah tipe seperti yang lain, tetapi C # tidak -double
adalah primitif dalam C #.sumber
#if
dan artefak lain yang tidak akan ada dalam kode terkompilasi. Plus jika itu direkayasa balik untukdouble
lalu mengapa tidak direkayasa balik untukint
ataulong
? Saya pikir ada alasan untuk kode sumber tetapi percaya bahwa penggunaan==
di dalam operator dikompilasi keCEQ
yang mencegah rekursi. Karena operator adalah operator "yang telah ditentukan" untuk tipe itu (dan tidak dapat ditimpa) aturan overload tidak berlaku.double
bukan bagian dari BCL - itu ada di perpustakaan yang terpisah, yang kebetulan dimasukkan dalam spesifikasi C #. Ya,==
akan dikompilasi ke aceq
, tapi itu masih berarti ini adalah hack kompiler yang tidak dapat Anda tiru dalam kode Anda sendiri, dan sesuatu yang bukan bagian dari spesifikasi C # (sepertifloat64
bidang padaDouble
struct). Ini bukan bagian kontrak dari C #, jadi tidak ada gunanya memperlakukannya seperti C # yang valid, bahkan jika itu dikompilasi dengan kompiler C #.double
dengan cara yang sama sepertiint
danlong
-int
danlong
merupakan tipe primitif yang harus didukung oleh semua bahasa .NET.float
,decimal
dandouble
tidak.Sumber tipe primitif bisa membingungkan. Pernahkah Anda melihat baris pertama dari
Double
struct?Biasanya Anda tidak dapat mendefinisikan struct rekursif seperti ini:
Tipe primitif memiliki dukungan asli mereka di CIL juga. Biasanya mereka tidak diperlakukan seperti tipe berorientasi objek. Double adalah nilai 64-bit jika digunakan seperti
float64
pada CIL. Namun, jika ditangani sebagai tipe .NET biasa, ini berisi nilai aktual dan berisi metode seperti jenis lainnya.Jadi yang Anda lihat di sini adalah situasi yang sama untuk operator. Biasanya jika Anda menggunakan tipe tipe ganda secara langsung, itu tidak akan pernah dipanggil. BTW, sumbernya terlihat seperti ini di CIL:
Seperti yang Anda lihat, tidak ada loop tanpa akhir (
ceq
instrumen digunakan alih-alih memanggilSystem.Double::op_Equality
). Jadi ketika ganda diperlakukan seperti objek, metode operator akan dipanggil, yang pada akhirnya akan menanganinya sebagaifloat64
tipe primitif pada level CIL.sumber
public struct MyNumber { internal MyNumber m_value; }
. Itu tidak bisa dikompilasi, tentu saja. Kesalahannya adalah kesalahan CS0523: Anggota Struct 'MyNumber.m_value' dari tipe 'MyNumber' menyebabkan siklus dalam tata letak structSaya melihat CIL dengan JustDecompile. Bagian dalam
==
akan diterjemahkan ke kode ce ce CIL . Dengan kata lain, ini adalah persamaan CLR primitif.Saya ingin tahu apakah kompiler C # akan referensi
ceq
atau==
operator ketika membandingkan dua nilai ganda. Dalam contoh sepele saya datang dengan (di bawah), itu digunakanceq
.Program ini:
menghasilkan CIL berikut (perhatikan pernyataan dengan label
IL_0017
):sumber
Seperti yang ditunjukkan dalam dokumentasi Microsoft untuk System.Runtime.Versioning Namespace: Jenis yang ditemukan di namespace ini dimaksudkan untuk digunakan dalam .NET Framework dan bukan untuk aplikasi pengguna. System.Runtime.Nersioning namespace berisi tipe canggih yang mendukung versi di implementasi berdampingan dari .NET Framework.
sumber
System.Runtime.Versioning
hubungannya denganSystem.Double
?