2009-07-07 39 views
19

C# có câu lệnh using, cụ thể cho các đối tượng IDisposable. Có lẽ, bất kỳ đối tượng nào được chỉ định trong câu lệnh using sẽ giữ một số loại tài nguyên cần được giải phóng một cách xác định.Thực hành không tốt? Sử dụng không chính thức câu lệnh sử dụng C#

Tuy nhiên, có vẻ như với tôi rằng có rất nhiều mẫu thiết kế trong lập trình có một đầu, kết thúc duy nhất và kết thúc, nhưng thiếu hỗ trợ ngôn ngữ nội tại. Cấu trúc using cung cấp cơ hội sử dụng tính năng được tích hợp sẵn của trình chỉnh sửa mã, ít nhất, rõ ràng và tự nhiên làm nổi bật phạm vi của thiết kế hoặc hoạt động đó.

Điều tôi lưu ý là loại hoạt động thường bắt đầu bằng phương pháp BeginXXX()EndXXX(), mặc dù có nhiều hương vị khác nhau, chẳng hạn như thực thi mã không đồng bộ có liên quan đến "bắt đầu" và "tham gia".

Lấy ví dụ ngây thơ này.

webDataOperation.Start(); 
GetContentFromHardDrive(); 
webDataOperation.Join(); 
// Perform operation that requires data from both sources 

Nếu phương thức bắt đầu trả lại đối tượng có phương pháp tham gia là IDisposable.Dispose.

using(webDataOperation.Start()) { 
    GetContentFromHardDrive(); 
} 
// Perform operation that requires data from both sources 

Hoặc, tốt hơn hết, những gì tôi đặc biệt có trong tâm trí: Tôi có một đối tượng đồ họa mà không có chuyên môn cao blitting và có một Begin()End() phương pháp (một thiết kế cũng có mặt trong DirectX và XNA). Thay vào đó ...

using(blitter.BlitOperation()) { 
    // Do work 
} 
// Use result 

Nó có vẻ là tự nhiên hơn và có thể đọc được, nhưng là không đi được, nhìn thấy vì nó sử dụng giao diện IDisposableusing tuyên bố cho các mục đích ngoài ý muốn? Nói cách khác, điều này có ngang bằng với việc o tải xuống toán tử theo cách không trực quan không?

+0

Tại sao không chỉ sử dụng {lúc bắt đầu và} ở cuối? Điều này cung cấp phạm vi mà không lạm dụng bằng cách sử dụng. – cja

Trả lời

20

Đây là một thực tiễn hoàn toàn có thể chấp nhận được. Đây được gọi là Các loại nhân tố và Framework Design Guidelines đề xuất chỉ thực hiện việc này.

Về cơ bản, nếu loại kết thúc hoạt động với một thời gian cụ thể, sử dụng IDisposable và câu lệnh sử dụng trở thành một điều thích hợp để xem xét.

Tôi cũng thực sự viết blog về số this specific topic here.

+0

Tìm tốt, nhưng bạn có lý do nào cho lập luận của mình không? Bài viết đề xuất nó, nhưng không thảo luận tại sao. – snarf

+0

Đó là bởi vì nó đảm bảo rằng EndOperation() luôn được gọi (thông qua Dispose()) –

+1

@Snarfblam: Về cơ bản, hoạt động chính nó hoạt động như một tài nguyên cần dọn dẹp. Đọc bài đăng trên blog của tôi để biết thêm chi tiết - đó là liên kết thứ hai. Tôi đặt nhiều hơn sự biện minh trong nó hơn tôi sẽ ở đây. –

2

Tôi nghĩ bạn nên sử dụng IDisposable cho mục đích của nó và không có gì khác. Đó là, nếu khả năng bảo trì quan trọng với bạn.

+1

John: Xem Hướng dẫn thiết kế khung về các loại nhân tố: http://blogs.msdn.com/brada/archive/2009/02/23/framework-design-guidelines-factored-types.aspx - Đây là một đề xuất cách tiếp cận để xử lý chúng. –

+0

Điều này tác động đến khả năng bảo trì như thế nào? Và đây có phải là lý do duy nhất mà bạn đề nghị tuân thủ nghiêm ngặt đối với canon không? – snarf

+0

Nó ảnh hưởng đến khả năng của các nhà phát triển khác để hiểu những gì đang xảy ra. Lý do của tôi cho các khuyến nghị bao gồm một thực tế là nó đủ khó để có được mọi người sử dụng IDisposable cho những lý do bình thường - chúng tôi không cần thêm những người thân. –

0

Tôi muốn nói là có thể chấp nhận được - trên thực tế, tôi đã sử dụng nó trong một số dự án mà tôi muốn có một hành động được kích hoạt ở cuối khối mã cụ thể.

Wes Deyer sử dụng nó trong LINQ mình để ASCII chương trình nghệ thuật, ông gọi nó là hành động dùng một lần (Wes hoạt động trên C đội # biên dịch - Tôi muốn tin tưởng phán đoán của mình: D):

http://blogs.msdn.com/wesdyer/archive/2007/02/23/linq-to-ascii-art.aspx

class ActionDisposable: IDisposable 
{ 
    Action action; 

    public ActionDisposable(Action action) 
    { 
     this.action = action; 
    } 

    #region IDisposable Members 

    public void Dispose() 
    { 
     this.action(); 
    } 

    #endregion 
} 

Bây giờ bạn có thể trở lại đó từ một chức năng, và làm điều gì đó như thế này:

using(ExtendedConsoleWriter.Indent()) 
{ 
    ExtendedConsoleWriter.Write("This is more indented"); 
} 

ExtendedConsoleWriter.Write("This is less indented"); 
+1

Wow, đó là một sự lạm dụng trắng trợn của tuyên bố sử dụng. Tôi sẽ không biết mã đó đang cố gắng làm gì mà không cần mở mã của ExtendedConsoleWriter. Nếu có các cuộc gọi đến IncreaseIndent và DecreaseIndent xung quanh mã, sẽ không có câu hỏi. –

+0

Tôi sẽ không sử dụng "sử dụng" cho điều này, nhưng, giả sử rằng có một từ khóa tương tự, và giao diện liên quan, chẳng hạn như "with": with (writer.Indent()) {stuff; }, nó sẽ là một cấu trúc convinent. – snarf

+3

Yikes, Ryan. Đó là một ví dụ được viết nhanh. Tôi đồng ý, tôi sẽ gọi nó là Tăng cường - trong trường hợp đó tôi sẽ không gọi nó là "lạm dụng trắng trợn của tuyên bố sử dụng". Đây là loại khái niệm có thể được nhìn thấy trong các giao dịch scoping trong suốt NET - có vẻ như bạn đang đi xuống một chút khắc nghiệt ở đó trên minutae. – Charles

5

đó là một mô hình phổ biến, nhưng cá nhân tôi, tôi tin rằng không có lý do gì để lạm dụng ID isposable như thế khi bạn có thể đạt được hiệu ứng tương tự theo cách rõ ràng hơn nhiều với các đại biểu vô danh và/hoặc lambdas; tức là:

blitter.BlitOperation(delegate 
{ 
    // your code 
}); 
+0

Và, nếu bạn muốn, nói rằng, luôn luôn làm công việc trong một chủ đề khác, bạn không cần xé bỏ mã của bạn (ví dụ, thiết kế này thêm giá trị) –

+0

Mặc dù tôi cũng thích mẫu này, phức tạp của riêng - trong một số trường hợp, nó có thể trở nên ít rõ ràng hơn là xử lý thông qua một câu lệnh sử dụng. Nhiều người (đặc biệt là cấp độ nhập cảnh) devs có vấn đề grokking lambdas. –

+0

Điều này cũng không cho phép an toàn tương đương, trong một số trường hợp. Ví dụ, bạn không thể sử dụng điều này bên trong của một phương pháp với tuyên bố lợi nhuận và được đảm bảo dọn dẹp ... –

6

Chỉ vì bạn có thể (hoặc vì Phil Haack nói không sao), không có nghĩa là bạn nên làm.

Nguyên tắc cơ bản của ngón tay cái: nếu tôi có thể đọc mã của bạn và hiểu nó đang làm gì và ý định của bạn là gì thì có thể chấp nhận được. Nếu, mặt khác, bạn cần giải thích những gì bạn đã làm, hoặc tại sao bạn đã làm nó, nó có lẽ sẽ đi du lịch lên các nhà phát triển cơ sở duy trì mã.

Có nhiều mẫu khác có thể thực hiện việc này bằng cách đóng gói tốt hơn.

Điểm mấu chốt: "kỹ thuật" này không mua gì cho bạn và chỉ hoạt động để gây nhầm lẫn cho các nhà phát triển khác.

+0

+1 cho lời khen ngợi về sự hiểu biết. Tôi hoàn toàn đồng ý - mặc dù, nói chung, tôi nghĩ việc sử dụng IDisposable cho các loại nhân tố cung cấp một phương tiện rất sạch sẽ, dễ hiểu trong việc xử lý chúng trong nhiều trường hợp ... nhưng đó là vấn đề viết mã của bạn một cách chính xác, cũng như rất rõ ràng về những gì bạn đang làm. tức là: TransactionScope trong BCL là tự nhiên đối với tôi - mã thụt lề trong câu trả lời khác ở đây là không. Giống như nhiều thứ, tôi nghĩ rằng điều này là hữu ích, nhưng có thể dễ dàng bị lạm dụng. –

+0

Đồng ý. Những gì làm việc, công trình. Thật dễ dàng để mang đi với các khái niệm thú vị, nhưng không có gì mà chỉ phục vụ để gây nhầm lẫn nên được sử dụng trong một dự án thế giới thực. – Charles

+0

Nếu nó có vẻ khó hiểu với tôi, tôi sẽ không hỏi. Bỏ qua sự nhầm lẫn tiềm năng, bạn có một giải pháp thay thế nào sẽ trở nên dễ hiểu hơn hoặc đơn giản hơn không? Tôi thích cách "sử dụng" là nội tuyến, thụt lề, và ngôn ngữ được hỗ trợ, và rõ ràng, cụ thể và rõ ràng đóng gói phạm vi. – snarf

12

Tôi khuyên bạn nên chống lại nó; niềm tin của tôi là mã đó là giao tiếp hiệu quả với người bảo trì mã, không phải trình biên dịch, và nên được viết với sự hiểu biết của người bảo trì. Tôi cố gắng sử dụng "chỉ sử dụng" để vứt bỏ tài nguyên, thường là tài nguyên không được quản lý.

Tôi thuộc thiểu số. Hầu hết mọi người dường như sử dụng "sử dụng" như một mục đích chung "Tôi muốn một số mã dọn dẹp để chạy ngay cả khi một ngoại lệ được ném" cơ chế.

Tôi không thích điều này bởi vì (1) chúng tôi đã có cơ chế cho điều đó, được gọi là "cố gắng cuối cùng", (2) nó sử dụng một tính năng cho mục đích không nhằm mục đích, và (3) mã dọn dẹp là rất quan trọng, vậy tại sao nó không hiển thị ở điểm mà nó được gọi? Nếu điều quan trọng thì tôi muốn có thể nhìn thấy nó.

+0

Dường như với tôi rằng việc sử dụng 'sử dụng' này là vô hạn chế. Ngoài sự tò mò, có bất kỳ tính năng ngôn ngữ nào khác trong C# nơi bạn muốn giới thiệu chống lại việc sử dụng nó hay không - mặc dù nó hoạt động hoàn hảo và rõ ràng - bởi vì sử dụng không phải là những gì nó được thiết kế? –

+0

@KirkWoll: Có. Tôi khuyên bạn nên sử dụng các tính năng ngôn ngữ * tất cả * được sử dụng cho một thứ khác với những gì chúng được thiết kế cho. –

+0

@EricLippert - tất nhiên, không phải lúc nào cũng rõ ràng tính năng ngôn ngữ được thiết kế cho cái gì. Trong thực tế, ngay cả trong trường hợp cụ thể của 'using', tôi nghĩ có một số dấu hiệu cho thấy tên" using "đã được chọn cụ thể bởi vì nó được dự đoán rằng feature _would not_ chỉ được sử dụng để xử lý các tài nguyên không được quản lý. – kvb

Các vấn đề liên quan