Mengapa .NET modul memisahkan nama file modul dari ruang nama?


9

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.slsmana $DIRada beberapa direktori tempat Anda menyimpan modul Skema Anda. xyz.slsadalah 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?


10
Perakitan dan mekanisme resolusi kelas .NET telah bekerja dengan baik selama lebih dari 10 tahun. Saya pikir Anda kurang memiliki kesalahpahaman mendasar (atau tidak cukup penelitian) tentang mengapa itu dirancang seperti itu - misalnya untuk mendukung pengarahan ulang perakitan dll pada waktu yang mengikat dan banyak mekanisme resolusi berguna lainnya
Kev

Saya cukup yakin bahwa resolusi DLL dari pernyataan menggunakan akan memecah eksekusi berdampingan. Juga jika ada 1 banding 1 Anda perlu 50 dll untuk semua ruang nama di mscorlib, atau mereka harus membuang ide ruang nama
Conrad Frix


@ gilles Terima kasih telah mengedit dan meningkatkan pertanyaan!
dharmatech

Beberapa poin Anda khusus untuk Visual Studio, dan membandingkannya dengan bahasa tidak cukup adil ( misalnya , referensi DLL di Proyek). Perbandingan yang lebih baik untuk lingkungan .NET penuh adalah Java + Eclipse.
Ross Patterson

Jawaban:


22

Anotasi berikut dalam Pedoman Desain Kerangka Kerja Bagian 3.3. Nama Majelis dan Dll menawarkan wawasan mengapa ruang nama dan majelis terpisah.

ABRAM BRAD Pada awal desain CLR kami memutuskan untuk memisahkan pandangan pengembang platform (namespaces) dari tampilan pengemasan dan penyebaran platform (assemblies). Pemisahan ini memungkinkan masing-masing dioptimalkan secara independen berdasarkan kriteria sendiri. Sebagai contoh, kita bebas untuk memfaktorkan ruang nama ke tipe grup yang terkait secara fungsional (misalnya, semua hal I / O di System.IO) sementara majelis dapat diperhitungkan untuk kinerja (waktu buka), penyebaran, servis, atau alasan versi .


Sepertinya sumber yang paling otoritatif sejauh ini. Terima kasih Conrad!
dharmatech

+1 untuk mendapatkan jawaban otoritatif terkutuk. Tetapi juga, setiap pertanyaan " mengapa C # do <X> " juga perlu dilihat melalui lensa " Java melakukan <X> sebagai berikut: ... ", karena C # adalah (secara eksplisit atau tidak) merupakan reaksi terhadap Sun dan Agenda Microsoft yang berbeda untuk Java pada Windows.
Ross Patterson

6

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 perpustakaan DAL.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 menambahkan DAL.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 usings 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.


Kasus kedua tidak memerlukan nama file untuk dipisahkan dari ruang nama (meskipun memungkinkan pemisahan seperti itu membuat pemisahan secara signifikan lebih mudah), karena majelis yang diberikan dapat dengan mudah memiliki banyak subfolder dan file. Selanjutnya, dimungkinkan juga untuk menanamkan banyak majelis ke dalam DLL yang sama (misalnya, menggunakan ILMerge ). Java works mengambil pendekatan ini.
Brian

2

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.


1
Mengenai pengelolaan dependensi Anda mungkin ingin melihat NuGet . Ini artikel yang bagus tentang hal itu dari Phil Haack
Conrad Frix

Dalam implementasi Skema R6RS saya telah menggunakan (Ikarus, Chez, dan Ypsilon, misalnya) dependensi ditangani secara otomatis, berdasarkan impor perpustakaan. Ketergantungan ditemukan dan jika perlu dikompilasi dan di-cache untuk impor di masa depan.
dharmatech

Akrab dengan Nuget, dan dengan demikian komentar saya bahwa itu "relatif belum matang"
JasonTrue
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.