'Sử dụng' có phù hợp trong bối cảnh không có gì để xử lý không?


9

Trong C #, usingcâu lệnh được sử dụng để xử lý theo cách xác định các tài nguyên mà không cần chờ người thu gom rác. Ví dụ, nó có thể được sử dụng để:

  • Loại bỏ các lệnh hoặc kết nối SQL,

  • Đóng luồng, giải phóng nguồn cơ bản như tệp,

  • Các yếu tố GDI + miễn phí,

  • Vân vân.

Tôi nhận thấy rằng nó usingđược sử dụng ngày càng nhiều trong trường hợp không có gì để xử lý, nhưng ở đó người gọi sẽ thuận tiện hơn khi viết một usingkhối thay vì hai lệnh riêng biệt.

Ví dụ:

  • MiniProfiler , được viết bởi nhóm Stack Overflow, sử dụng usingđể biểu thị các khối vào hồ sơ:

    using (profiler.Step("Name goes here"))
    {
        this.DoSomethingUseful(i - 1);
    }

    Một cách tiếp cận khác là có hai khối:

    var p = profiler.Start("Name goes here");
    this.DoSomethingUseful(i - 1);
    profiler.Stop(p);

    Một cách tiếp cận khác là sử dụng các hành động:

    profiler.Step("Name goes here", () => this.DoSomethingUseful(i - 1));
  • ASP.NET MVC cũng được chọn usingcho các hình thức:

    <% using (Html.BeginForm())
       { %>
           <label for="firstName">Name:</label>
           <%= Html.TextBox("name")%>
           <input type="submit" value="Save" />    
    <% } %>

Sử dụng như vậy có phù hợp không? Làm thế nào để biện minh cho nó, cho rằng có một số nhược điểm:

  • Người mới bắt đầu sẽ bị mất, vì cách sử dụng như vậy không tương ứng với cách sử dụng được giải thích trong sách và đặc tả ngôn ngữ,

  • Các mã nên được biểu cảm. Ở đây, tính biểu cảm bị ảnh hưởng, vì cách sử dụng thích hợp usinglà cho thấy đằng sau, có một tài nguyên, như luồng, kết nối mạng hoặc cơ sở dữ liệu sẽ được phát hành mà không cần chờ người thu gom rác.


2
Cách tiếp cận có hai câu lệnh riêng lẻ gặp phải cùng một vấn đề như quản lý tài nguyên ngây thơ mà không có using: Câu lệnh sau (ví dụ profiler.Stop(p)) không được đảm bảo để được thực thi khi đối mặt với các ngoại lệ và luồng điều khiển.

1
@delnan: điều này giải thích tại sao MiniProfiler tránh cách tiếp cận thứ hai. Nhưng cái thứ hai nên tránh trong mọi trường hợp, vì nó khó viết và duy trì.
Arseni Mourzenko



Câu trả lời:


7

Tuyên bố cuối cùng của bạn - rằng "cách sử dụng phù hợp là cho thấy đằng sau, có một tài nguyên, như luồng, kết nối mạng hoặc cơ sở dữ liệu sẽ được phát hành mà không cần chờ trình thu gom rác" là không chính xác và lý do là được cung cấp tại tài liệu cho giao diện IDis Dùng: http://msdn.microsoft.com/en-us/l Library / system.idis Dùng.aspx

Việc sử dụng chính của giao diện này là để giải phóng các tài nguyên không được quản lý.

Vì vậy, nếu lớp của bạn sử dụng bất kỳ nguồn lực không được quản lý nào, sẽ không có vấn đề gì khi bạn có thể hoặc không muốn xảy ra với GC - không có gì để làm với GC vì tài nguyên không được quản lý không phải là GC ( https: // stackoverflow. com / câu hỏi / 3607213 / what-is-mean-by-Managed-vs-unmanaged-resource-in-net ).

Vì vậy, mục đích của việc "sử dụng" không phải là để tránh chờ đợi trên GC, nó buộc phải giải phóng các tài nguyên không được quản lý đó ngay bây giờ , trước khi cá thể lớp vượt ra khỏi phạm vi và nó được gọi là Hoàn thiện. Điều này rất quan trọng vì những lý do rõ ràng - một tài nguyên không được quản lý có thể có sự phụ thuộc vào các tài nguyên không được quản lý khác và nếu chúng bị xử lý (hoặc hoàn thành) theo thứ tự sai Điều xấu có thể xảy ra.

Vì vậy, nếu lớp được kích hoạt trong khối sử dụng sử dụng bất kỳ tài nguyên không được quản lý nào, thì câu trả lời là có - nó phù hợp.

Lưu ý rằng IDis Dùng không phải là quy định về việc nó chỉ để phát hành các tài nguyên không được quản lý, tuy nhiên - đây chỉ là mục đích chính . Có thể trường hợp tác giả của lớp có một số hành động mà họ muốn thực thi xảy ra vào một thời điểm nhất định và thực hiện IDis Dùng có thể là một cách để điều đó xảy ra, nhưng liệu đó có phải là một giải pháp tao nhã hay không chỉ được trả lời trên cơ sở từng trường hợp.

Dù thế nào đi nữa, việc sử dụng "sử dụng" ngụ ý rằng lớp thực hiện IDis Dùng một lần, do đó, nó không vi phạm tính biểu cảm của mã; nó làm cho nó khá rõ ràng những gì đang xảy ra, trên thực tế.


"Việc sử dụng chính của giao diện này là để giải phóng các tài nguyên không được quản lý." Tôi nói tài liệu của Microsoft hút về điểm đó. Có thể đó là cách họ sử dụng nó trong FCL, nhưng các nhà phát triển ứng dụng đã sử dụng nó cho nhiều thứ trong gần một thập kỷ nay - một số vì lý do chính đáng, một số vì lý do không tốt. Nhưng trích dẫn đoạn tài liệu của họ hoàn toàn không trả lời câu hỏi của OP.
Jesse C. Choper

1
Lưu ý đoạn cuối thứ hai của tôi - mục đích chính không phải là mục đích duy nhất . IDis Dùng có thể được triển khai vì bất kỳ lý do gì, và nó cung cấp một giao diện đã biết với hành vi và cách sử dụng dự kiến, vì vậy khi bạn nhìn thấy nó, bạn biết rằng có một cái gì đó khác cần phải xảy ra trước khi cá thể có thể thoát khỏi phạm vi. Vấn đề là: nếu nó triển khai IDis Dùng một lần, thì sử dụng là phù hợp; mọi thứ khác chỉ là lời mở đầu.
Maximus Minimus

1
Về cơ bản, bạn đang nói rằng mục đích không phải là chờ đợi cho GC, nó tránh việc chờ đợi người hoàn thiện. Nhưng tôi không thấy sự khác biệt: quyết toán được gọi bởi GC.
Svick

GC hoạt động theo cách không xác định - bạn không biết khi nào nó sẽ được gọi. GC cũng chỉ gọi bộ hoàn thiện, nhưng nó không tự xử lý - tài nguyên thực tế nằm ngoài tầm kiểm soát của GC. Với việc sử dụng (1) đối tượng được sắp xếp đúng phạm vi và (2) việc xử lý được biết là xảy ra tại một thời điểm đã biết - khi nó thoát khỏi phạm vi của khối sử dụng, tài nguyên không được quản lý sẽ biến mất (hoặc - trường hợp xấu nhất - được trao qua một cái gì đó khác sẽ phá hủy nó). Thực sự, đây chỉ là nitpicking; đọc tài liệu, đó là tất cả không cần phải lặp lại.
Maximus Minimus

8

Việc sử dụng usingngụ ý sự hiện diện của một Dispose()phương pháp. Các lập trình viên khác sẽ cho rằng một phương thức như vậy tồn tại trên đối tượng. Do đó, nếu một đối tượng không dùng một lần, bạn không nên sử dụng usingnó.

Mã rõ ràng là vua. Hoặc bỏ qua using, hoặc thực hiện IDisposabletrên đối tượng.

MiniProfiler dường như đang sử dụng usingnhư một cơ chế để "rào cản" mã được định hình. Có một số công đức cho điều này; có lẽ, MiniProfiler đang gọi Dispose()để dừng bộ hẹn giờ hoặc bộ hẹn giờ bị dừng khi đối tượng MiniProfiler đi ra khỏi phạm vi.

Tổng quát hơn, bạn sẽ gọi usingkhi một số loại quyết toán cần phải tự động xảy ra. Tài liệu cho html.BeginFormcác trạng thái rằng, khi phương thức được sử dụng trong một usingcâu lệnh, nó sẽ hiển thị </form>thẻ đóng ở cuối usingkhối.

Điều đó không nhất thiết có nghĩa là nó vẫn không lạm dụng.


4
Cho đến nay rất rõ ràng. Câu hỏi là, khi nào (nếu có) có phù hợp để thực hiện IDisposable/ Dispose()mặc dù không có bất cứ điều gì để xử lý theo nghĩa OP đang mô tả?

2
Tôi nghĩ rằng tôi đã làm rõ điều đó; có không phải là một thời gian khi điều đó là thích hợp.
Robert Harvey

1
Quan điểm của tôi là, Disposeđược yêu cầu usingđể có ý nghĩa ở tất cả (bất kể nó có phù hợp hay không). Các ví dụ của OP đều thực hiện Dispose, phải không? Và IIUC, câu hỏi đặt ra là liệu có đúng khi các lớp này sử dụng Dispose, chứ không phải là một số phương thức khác (như Stop()đối với MiniProfiler).

1
Vâng, nhưng đó không phải là câu hỏi. Câu hỏi là liệu đó có phải là một ý tưởng tốt để làm như vậy.

2
@delnan: Một lần nữa, tôi nghĩ rằng tôi đã làm rõ điều đó. Nếu nó làm cho ý định mã của bạn rõ ràng hơn, thì đó là một ý tưởng tốt. Nếu không, nó không.
Robert Harvey

1

usinglà lỗi và ngoại lệ an toàn. Nó đảm bảo Dispose()sẽ được gọi là bất chấp mọi lỗi lầm mà lập trình viên mắc phải. Nó không can thiệp vào việc bắt hoặc xử lý các trường hợp ngoại lệ, nhưng Dispose()phương thức được thực hiện đệ quy lên ngăn xếp khi ném ngoại lệ.

Các đối tượng thực hiện IDisposenhưng không có gì để xử lý khi chúng được thực hiện. Là tốt nhất, tương lai chứng minh thiết kế của họ. Vì vậy, bạn không cần phải cấu trúc lại mã nguồn của mình, khi trong tương lai, sau đó họ cần phải xử lý một cái gì đó.

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.