Tại sao các mô-đun .NET tách tên tệp mô-đun khỏi không gian tên?


9

Khi triển khai ngôn ngữ lập trình Scheme (tiêu chuẩn R6RS) tôi có thể nhập một mô-đun như sau:

(import (abc def xyz))

Hệ thống sẽ cố gắng tìm kiếm một tập tin $DIR/abc/def/xyz.slstrong đó $DIRcó một số thư mục mà bạn giữ các mô-đun Scheme. xyz.slslà mã nguồn cho mô-đun và nó được biên dịch nhanh chóng nếu cần thiết.

Các hệ thống mô-đun Ruby, Python và Perl tương tự nhau về mặt này.

C # mặt khác có liên quan nhiều hơn một chút.

Đầu tiên, bạn có các tệp dll mà bạn phải tham chiếu trên cơ sở từng dự án. Bạn phải tham khảo từng cái một cách rõ ràng. Điều này có liên quan nhiều hơn nói, thả các tập tin dll trong một thư mục và có C # chọn chúng theo tên.

Thứ hai, không có sự tương ứng đặt tên một-một giữa tên tệp dll và các không gian tên được cung cấp bởi dll. Tôi có thể đánh giá cao sự linh hoạt này, nhưng nó cũng có thể vượt khỏi tầm tay (và có).

Để làm điều này cụ thể, sẽ rất tuyệt nếu, khi tôi nói điều này using abc.def.xyz;, C # sẽ cố gắng tìm một tệp abc/def/xyz.dll, trong một số thư mục mà C # biết để tìm trong (có thể định cấu hình trên cơ sở từng dự án).

Tôi thấy cách xử lý các mô-đun Ruby, Python, Perl, Scheme thanh lịch hơn. Có vẻ như các ngôn ngữ mới nổi có xu hướng đi với thiết kế đơn giản hơn.

Tại sao thế giới .NET / C # thực hiện mọi thứ theo cách này, với một mức độ bổ sung cao hơn?


10
Cơ chế phân giải và lắp ráp lớp của .NET đã hoạt động tốt trong hơn 10 năm. Tôi nghĩ rằng bạn đang thiếu một sự hiểu lầm cơ bản (hoặc không đủ nghiên cứu) về lý do tại sao nó được thiết kế theo cách đó - ví dụ: để hỗ trợ chuyển hướng lắp ráp vv vào thời điểm ràng buộc và nhiều cơ chế giải quyết hữu ích khác
Kev

Tôi khá chắc chắn rằng độ phân giải DLL từ một câu lệnh sử dụng sẽ phá vỡ sự thực thi song song. Ngoài ra, nếu có 1 đến 1, bạn cần 50 dll cho tất cả các không gian tên trong mscorlib, hoặc họ sẽ phải bỏ ý tưởng về không gian tên
Conrad Frix

Một mức độ bổ sung? Hmmmmm
user541686

@gilles Cảm ơn bạn đã chỉnh sửa và cải thiện câu hỏi!
dharmatech

Một số điểm của bạn rất đặc biệt với Visual Studio và việc so sánh chúng với một ngôn ngữ không công bằng ( ví dụ: tham chiếu DLL trong Dự án). Một so sánh tốt hơn cho môi trường .NET đầy đủ sẽ là Java + Eclipse.
Ross Patterson

Câu trả lời:


22

Chú thích sau đây trong Hướng dẫn thiết kế khung Phần 3.3. Tên của Hội đồng và DLL cung cấp cái nhìn sâu sắc về lý do tại sao không gian tên và hội đồng là riêng biệt.

BRAD ABRAM Trước khi thiết kế CLR, chúng tôi đã quyết định tách biệt chế độ xem của nhà phát triển nền tảng (không gian tên) từ chế độ đóng gói và triển khai của nền tảng (cụm). Sự tách biệt này cho phép mỗi cái được tối ưu hóa độc lập dựa trên các tiêu chí riêng của nó. Ví dụ: chúng tôi có thể tự do đặt các không gian tên cho các loại nhóm có liên quan đến chức năng (ví dụ: tất cả các công cụ I / O trong System.IO) trong khi các hội đồng có thể được xác định cho hiệu suất (thời gian tải), triển khai, phục vụ hoặc phiên bản .


Có vẻ như là nguồn có thẩm quyền nhất cho đến nay. Cảm ơn anh!
dharmatech

+1 để nhận được câu trả lời có thẩm quyền. Nhưng ngoài ra, mọi câu hỏi " tại sao C # do <X> " cũng cần được xem qua lăng kính của " Java không <X> như sau: ... ", bởi vì C # là (rõ ràng hay không) phản ứng với Sun's và Các chương trình nghị sự khác nhau của Microsoft cho Java trên Windows.
Ross Patterson

6

Nó bổ sung tính linh hoạt và cho phép tải các thư viện (cái mà bạn gọi là các mô-đun trong câu hỏi của bạn) theo yêu cầu.

Một không gian tên, nhiều thư viện:

Một trong những lợi thế là tôi có thể dễ dàng thay thế một thư viện bằng một thư viện khác. Giả sử tôi có một không gian tên MyCompany.MyApplication.DALvà một thư viện DAL.MicrosoftSQL.dllchứa tất cả các truy vấn SQL và các nội dung khác có thể dành riêng cho cơ sở dữ liệu. Nếu tôi muốn ứng dụng tương thích với Oracle, tôi chỉ cần thêm DAL.Oracle.dll, giữ nguyên không gian tên. Từ bây giờ, tôi có thể phân phối ứng dụng với một thư viện cho những khách hàng cần khả năng tương thích với Microsoft SQL Server và với thư viện khác cho những khách hàng sử dụng Oracle.

Thay đổi không gian tên ở cấp độ này sẽ dẫn đến mã trùng lặp hoặc cần phải thay đổi tất cả các usingmã bên trong mã nguồn cho mỗi cơ sở dữ liệu.

Một thư viện, nhiều không gian tên:

Có một số không gian tên trong một thư viện cũng là lợi thế về khả năng đọc. Nếu, trong một lớp, tôi chỉ sử dụng một trong các không gian tên, tôi chỉ đặt cái này ở đầu tệp.

  • Có tất cả các không gian tên của một thư viện lớn sẽ khá khó hiểu cho cả người đọc mã nguồn và cho chính người viết, với Intellisense có quá nhiều điều để đề xuất trong một bối cảnh nhất định.

  • Có các thư viện nhỏ hơn, một thư viện cho mỗi tệp sẽ có tác động hiệu năng: mỗi thư viện phải được tải theo yêu cầu trong bộ nhớ và được xử lý bởi máy ảo khi chạy ứng dụng; tải ít tập tin hơn có nghĩa là hiệu suất tốt hơn một chút.


Trường hợp thứ hai không yêu cầu các tên tệp phải tách biệt với các không gian tên (mặc dù việc cho phép phân tách như vậy làm cho việc phân tách dễ dàng hơn đáng kể), vì một tập hợp đã cho có thể dễ dàng có nhiều thư mục con và tệp. Hơn nữa, cũng có thể nhúng nhiều tập hợp vào cùng một DLL (ví dụ: sử dụng ILMerge ). Các công trình Java có cách tiếp cận này.
Brian

2

Có vẻ như bạn đang chọn quá tải thuật ngữ của cả "không gian tên" và "mô-đun". Không có gì ngạc nhiên khi bạn thấy mọi thứ là "gián tiếp" khi chúng không phù hợp với định nghĩa của bạn.

Trong hầu hết các ngôn ngữ hỗ trợ không gian tên, bao gồm C #, không gian tên không phải là mô-đun. Một không gian tên là một cách để phạm vi tên. Các mô-đun là một cách của hành vi phạm vi.

Nói chung, trong khi thời gian chạy .Net hỗ trợ ý tưởng về một mô-đun (với định nghĩa hơi khác so với mô-đun bạn đang sử dụng ngầm), thì nó hiếm khi được sử dụng; Tôi chỉ thấy nó được sử dụng trong các dự án được xây dựng trong SharpDevelop, hầu hết để bạn có thể xây dựng một DLL duy nhất từ ​​các mô-đun được xây dựng bằng các ngôn ngữ khác nhau. Thay vào đó, chúng tôi xây dựng thư viện bằng thư viện được liên kết động.

Trong C #, các không gian tên giải quyết mà không có bất kỳ "lớp gián tiếp" nào, miễn là tất cả chúng đều nằm trong cùng một nhị phân; bất kỳ yêu cầu nào là trách nhiệm của trình biên dịch và trình liên kết mà bạn không cần phải suy nghĩ nhiều. Khi bạn bắt đầu xây dựng một dự án với nhiều phụ thuộc, bạn sẽ tham khảo các thư viện bên ngoài. Khi dự án của bạn đã thực hiện một tham chiếu đến một thư viện bên ngoài (DLL), trình biên dịch sẽ tìm thấy nó cho bạn.

Trong Lược đồ, nếu bạn cần tải một thư viện bên ngoài, bạn phải làm một cái gì đó giống như (#%require (lib "mylib.ss"))trước tiên, hoặc sử dụng giao diện chức năng nước ngoài trực tiếp, như tôi nhớ. Nếu bạn đang sử dụng các tệp nhị phân bên ngoài, bạn có cùng số lượng công việc để giải quyết các tệp nhị phân bên ngoài. Có thể bạn đã sử dụng hầu hết các thư viện thường được sử dụng đến mức có một shim dựa trên Scheme tóm tắt từ bạn, nhưng nếu bạn phải viết tích hợp của riêng mình với thư viện bên thứ 3, về cơ bản bạn sẽ phải thực hiện một số công việc để "tải " thư viện.

Trong Ruby, Mô-đun, Không gian tên và Tên tệp thực sự ít được kết nối hơn bạn tưởng; LOAD_PATH làm cho mọi thứ hơi phức tạp và khai báo Mô-đun có thể ở bất cứ đâu. Python có lẽ gần gũi hơn để làm mọi thứ theo cách bạn nghĩ bạn đang thấy trong Scheme, ngoại trừ việc các thư viện bên thứ 3 trong C vẫn thêm một nếp nhăn (nhỏ).

Ngoài ra, các ngôn ngữ được nhập động như Ruby, Python và Lisp thường không có cùng cách tiếp cận với "hợp đồng" như các ngôn ngữ được nhập tĩnh. Trong các ngôn ngữ được gõ động, bạn thường chỉ thiết lập một loại "Thỏa thuận của quý ông" mà mã đó sẽ đáp ứng với một số phương thức nhất định và nếu các lớp của bạn có vẻ nói cùng một ngôn ngữ, tất cả đều tốt. Các ngôn ngữ được nhập tĩnh có các cơ chế bổ sung để thực thi các quy tắc này tại thời gian biên dịch. Trong C #, sử dụng hợp đồng như vậy cho phép bạn cung cấp ít nhất các đảm bảo hữu ích vừa phải về việc tuân thủ các giao diện này, cho phép bạn đóng gói các plugin và thay thế với một mức độ đảm bảo tính phổ biến vì tất cả các bạn đều biên dịch theo cùng một hợp đồng. Trong Ruby hoặc Scheme, bạn xác minh các thỏa thuận này bằng cách viết các bài kiểm tra hoạt động trong thời gian chạy.

Có một lợi ích hiệu suất có thể đo được từ các đảm bảo thời gian biên dịch này, vì một yêu cầu phương thức không yêu cầu gửi kép. Để có được những lợi ích này trong một cái gì đó như Lisp, Ruby, JavaScript hoặc các nơi khác, hiện tại vẫn còn các cơ chế hơi kỳ lạ của các lớp biên dịch tĩnh chỉ trong các máy ảo chuyên dụng.

Một điều mà hệ sinh thái C # vẫn có sự hỗ trợ tương đối non nớt là việc quản lý các phụ thuộc nhị phân này; Java đã có Maven trong vài năm để đảm bảo rằng bạn có tất cả các phụ thuộc cần thiết của mình, trong khi C # vẫn có một cách tiếp cận giống như MAKE khá nguyên thủy, bao gồm chiến lược đặt các tệp vào đúng vị trí trước thời hạn.


1
Về việc quản lý các phụ thuộc mà bạn có thể muốn xem qua NuGet . Đây là một bài viết hay về nó từ Phil Haack
Conrad Frix

Trong các triển khai Đề án R6RS mà tôi đã sử dụng (chẳng hạn như Ikarus, Chez và Ypsilon), các phụ thuộc được xử lý tự động, dựa trên việc nhập thư viện. Các phụ thuộc được tìm thấy và nếu cần thiết được biên dịch và lưu trữ cho các lần nhập trong tương lai.
dharmatech

Quen thuộc với Nuget, và do đó tôi nhận xét rằng nó "tương đối non nớt"
JasonTrue
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.