Dalam implementasi bahasa pemrograman Skema (standar R6RS) saya dapat mengimpor modul sebagai berikut:
(import (abc def xyz))
Sistem akan mencoba mencari file di $DIR/abc/def/xyz.sls
mana $DIR
ada beberapa direktori tempat Anda menyimpan modul Skema Anda. xyz.sls
adalah kode sumber untuk modul dan dikompilasi dengan cepat jika perlu.
Sistem modul Ruby, Python, dan Perl serupa dalam hal ini.
C # di sisi lain sedikit lebih terlibat.
Pertama, Anda memiliki file dll yang harus Anda referensi berdasarkan per proyek. Anda harus merujuk masing-masing secara eksplisit. Ini lebih terlibat daripada mengatakan, menjatuhkan file dll dalam direktori dan meminta C # mengambilnya berdasarkan nama.
Kedua, Tidak ada korespondensi penamaan satu-ke-satu antara nama file dll, dan ruang nama yang ditawarkan oleh dll. Saya bisa menghargai fleksibilitas ini, tetapi juga bisa lepas kendali (dan sudah).
Untuk membuat ini konkret, alangkah baiknya jika, ketika saya mengatakan ini using abc.def.xyz;
, C # akan mencoba untuk menemukan file abc/def/xyz.dll
, di beberapa direktori yang C # tahu untuk melihat (dapat dikonfigurasi berdasarkan per proyek).
Saya menemukan cara penanganan modul Ruby, Python, Perl, Skema lebih elegan. Tampaknya bahasa yang muncul cenderung cocok dengan desain yang lebih sederhana.
Mengapa dunia .NET / C # melakukan hal-hal dengan cara ini, dengan tingkat tipuan ekstra?
sumber
Jawaban:
Anotasi berikut dalam Pedoman Desain Kerangka Kerja Bagian 3.3. Nama Majelis dan Dll menawarkan wawasan mengapa ruang nama dan majelis terpisah.
sumber
Itu menambah fleksibilitas dan memungkinkan untuk memuat perpustakaan (apa yang Anda sebut modul dalam pertanyaan Anda) berdasarkan permintaan.
Satu namespace, banyak perpustakaan:
Salah satu kelebihannya adalah saya dapat dengan mudah mengganti satu perpustakaan dengan yang lain. Katakanlah saya memiliki namespace
MyCompany.MyApplication.DAL
, dan perpustakaanDAL.MicrosoftSQL.dll
, yang berisi semua pertanyaan SQL dan hal-hal lain yang mungkin khusus untuk database. Jika saya ingin aplikasi tersebut kompatibel dengan Oracle, saya hanya menambahkanDAL.Oracle.dll
, menjaga namespace yang sama. Mulai sekarang, saya dapat mengirimkan aplikasi dengan satu pustaka untuk pelanggan yang membutuhkan kompatibilitas dengan Microsoft SQL Server, dan dengan pustaka lainnya untuk pelanggan yang menggunakan Oracle.Mengubah ruang nama pada tingkat ini akan menyebabkan kode duplikat, atau keharusan untuk pergi dan mengubah semua
using
s di dalam kode sumber untuk setiap database.Satu perpustakaan, beberapa ruang nama:
Memiliki beberapa ruang nama di satu perpustakaan juga menguntungkan dalam hal keterbacaan. Jika, di kelas, saya hanya menggunakan salah satu ruang nama, saya hanya menempatkan ini di bagian atas file.
Memiliki semua ruang nama pada perpustakaan besar akan agak membingungkan baik untuk orang yang membaca kode sumber dan untuk penulis sendiri, dengan Intellisense memiliki terlalu banyak hal untuk disarankan dalam konteks yang diberikan.
Memiliki perpustakaan yang lebih kecil, satu perpustakaan per file akan memiliki dampak kinerja: setiap perpustakaan harus dimuat sesuai permintaan dalam memori dan diproses oleh mesin virtual saat menjalankan aplikasi; lebih sedikit file untuk dimuat berarti kinerja yang sedikit lebih baik.
sumber
Sepertinya Anda memilih untuk membebani terminologi "namespace" dan "modul." Seharusnya tidak mengherankan bahwa Anda melihat sesuatu sebagai "tidak langsung" ketika mereka tidak sesuai dengan definisi Anda.
Di sebagian besar bahasa yang mendukung ruang nama, termasuk C #, namespace bukan modul. Namespace adalah cara pelingkupan nama. Modul adalah cara pelingkupan perilaku.
Secara umum, sementara .Net runtime mendukung gagasan modul (dengan definisi yang sedikit berbeda dari yang Anda gunakan secara implisit), itu agak jarang digunakan; Saya hanya melihatnya digunakan dalam proyek yang dibangun di SharpDevelop, sebagian besar sehingga Anda dapat membangun DLL tunggal dari modul yang dibangun dalam bahasa yang berbeda. Sebagai gantinya, kami membangun perpustakaan menggunakan perpustakaan yang terhubung secara dinamis.
Dalam C #, namespaces menyelesaikan tanpa "lapisan tipuan" selama mereka semua dalam biner yang sama; tipuan apa pun yang diperlukan adalah tanggung jawab kompiler dan tautan yang tidak perlu Anda pikirkan. Setelah Anda mulai membangun proyek dengan banyak dependensi, Anda kemudian merujuk pustaka eksternal. Setelah proyek Anda membuat referensi ke perpustakaan eksternal (DLL), kompiler menemukannya untuk Anda.
Dalam Skema, jika Anda perlu memuat pustaka eksternal, Anda harus melakukan sesuatu seperti
(#%require (lib "mylib.ss"))
pertama, atau menggunakan antarmuka fungsi asing secara langsung, seingat saya. Jika Anda menggunakan binari eksternal, Anda memiliki jumlah pekerjaan yang sama untuk menyelesaikan binari eksternal. Kemungkinannya, Anda sebagian besar telah menggunakan pustaka yang sangat umum digunakan sehingga ada shim berbasis Skema yang mengabstraksikan itu dari Anda, tetapi jika Anda harus menulis integrasi Anda sendiri dengan pustaka pihak ketiga, pada dasarnya Anda harus melakukan beberapa pekerjaan untuk "memuat " Perpustakaan.Di Ruby, Modul, Ruang nama, dan Nama File sebenarnya jauh lebih sedikit terhubung daripada yang Anda duga; LOAD_PATH membuat segalanya menjadi sedikit rumit, dan deklarasi Modul bisa di mana saja. Python mungkin lebih dekat untuk melakukan hal-hal seperti yang Anda pikir Anda lihat di Skema, kecuali bahwa perpustakaan pihak ke-3 di C masih menambahkan kerutan (kecil).
Selain itu, bahasa yang diketik secara dinamis seperti Ruby, Python dan Lisp biasanya tidak memiliki pendekatan yang sama dengan "kontrak" seperti bahasa yang diketik secara statis. Dalam bahasa yang diketik secara dinamis, Anda biasanya hanya membuat semacam "persetujuan Gentleman" bahwa kode akan menanggapi metode tertentu, dan jika kelas Anda tampaknya berbicara dalam bahasa yang sama, semuanya baik. Bahasa yang diketik secara statis memiliki mekanisme tambahan untuk menegakkan aturan-aturan ini pada waktu kompilasi. Dalam C #, menggunakan kontrak semacam itu memungkinkan Anda untuk memberikan setidaknya setidaknya jaminan kepatuhan yang cukup berguna untuk antarmuka ini, yang memungkinkan Anda untuk membundel plugins dan substitusi dengan beberapa derajat jaminan kesamaan karena Anda semua mengkompilasi terhadap kontrak yang sama. Di Ruby atau Skema, Anda memverifikasi perjanjian ini dengan menulis tes yang berfungsi saat runtime.
Ada manfaat kinerja yang dapat diukur dari jaminan waktu kompilasi ini, karena pemanggilan metode tidak memerlukan pengiriman ganda. Untuk mendapatkan manfaat ini dalam sesuatu seperti Lisp, Ruby, JavaScript, atau di tempat lain, apa yang sekarang masih merupakan mekanisme eksotis dari kelas kompilasi statis tepat waktu dalam VM khusus diperlukan.
Satu hal yang ekosistem C # masih memiliki dukungan yang relatif belum matang adalah pengelolaan dependensi biner ini; Java telah memiliki Maven selama beberapa tahun untuk memastikan Anda memiliki semua dependensi yang diperlukan, sedangkan C # masih memiliki pendekatan seperti MAKE yang cukup primitif yang melibatkan penempatan file secara strategis di tempat yang tepat sebelumnya.
sumber