2009-04-03 65 views
7

Tôi hơi mới với C#, quen với ngôn ngữ kịch bản hơn. Tôi thích ý tưởng 'sử dụng', bạn khởi tạo một đối tượng, và sau đó bạn hoạt động trong phạm vi của nó miễn là bạn cần nó, sau đó bạn để nó vứt bỏ chính nó khi nó được thực hiện mục đích của nó.Làm cách nào để biết địa điểm tốt nhất để sử dụng 'sử dụng'?

Nhưng, điều đó không tự nhiên đối với tôi. Khi mọi người chỉ cho tôi những ví dụ sử dụng nó, tôi nhận ra nó là một công cụ tốt cho công việc, nhưng nó không bao giờ xảy ra với tôi để giải quyết vấn đề với nó trong lập trình của riêng tôi.

Làm thế nào tôi có thể nhận ra các địa điểm tốt để sử dụng using và cách sử dụng nó cùng với các khối try-catch. Họ có đi vào trong khối, hay bạn thường muốn đính kèm một câu lệnh sử dụng trong một khối thử?

Trả lời

9

Tôi hiếm khi viết try/catch khối - hầu hết trường hợp ngoại lệ được ném lên đến (gần) phía trên cùng của ngăn xếp. Nếu tôi do cần một khối try/catch, tôi không chắc chắn rằng tôi đặc biệt nhất quán giữa việc đặt nó bên trong câu lệnh using so với bên ngoài. Nó thực sự phụ thuộc vào việc bạn muốn tài nguyên được xử lý trước khi mã xử lý ngoại lệ của bạn được chạy.

Nếu bạn đang hỏi về khi bạn nên viết using báo cáo - bất cứ lúc nào bạn "sở hữu" một đối tượng mà thực hiện IDisposable (trực tiếp hoặc gián tiếp thông qua thừa kế) và kiểm soát cuộc đời của mình. Đó thường là một đối tượng sử dụng tài nguyên không được quản lý như một tập tin xử lý hoặc kết nối mạng. Nó không phải lúc nào cũng hiển nhiên, nhưng bạn học qua kinh nghiệm. Hầu như mọi thứ để thực hiện với IO sẽ được sử dụng một lần và các điều khiển Windows (đối với phông chữ vv) tương tự nhau.

+0

Để làm rõ các đối tượng thực hiện IDisposable trực tiếp hoặc thông qua thừa kế (như danieltalsky là "hơi mới với C#"). –

+0

Cảm ơn Jonathan - đã chỉnh sửa một cách thích hợp. –

+0

Vâng, bởi "hơi mới với C#" tôi có nghĩa là, "đã được viết mã thương mại trong C# cho ít hơn 8 tháng". Tôi đã, ví dụ, đã quen thuộc với kết nối của "sử dụng" để "IDisposable" mà tôi đề cập đến trong câu hỏi. Đó là phần "kiểm soát trọn đời" mà tôi đã mất tích. – danieltalsky

18

using chỉ có thể được sử dụng với các loại triển khai IDisposable; nó đảm bảo rằng phương thức Dispose() sẽ được gọi ngay cả khi xảy ra lỗi.

Mã này:

using (MyDisposableType x = new MyDisposableType()) 
{ 
    // use x 
} 

tương đương với điều này:

MyDisposableType x = new MyDisposableType(); 
try 
{ 
    // use x 
} 
finally 
{ 
    x.Dispose(); 
} 
+0

Vì vậy, ... sử dụng nó trên bất kỳ đối tượng nào triển khai IDisposable? Luôn luôn? – danieltalsky

+0

Và còn xử lý ngoại lệ thì sao? – danieltalsky

+0

sẽ downvoters xin vui lòng để lại một bình luận. Cảm ơn. –

1

gì Mitch nói, cộng với ..

Bạn có thể sử dụng câu lệnh sử dụng bên ngoài hoặc bên trong một khối try..catch nó thực sự sẽ phụ thuộc vào những gì bạn đang cố gắng để đạt được tức là cho dù bạn một cách hợp lý mong đợi một cái gì đó để ném một ngoại lệ trong khi sử dụng một đối tượng cụ thể mà bạn định khôi phục từ, ví dụ.

Bằng cùng một mã thông báo, bạn cũng có thể Vứt bỏ một đối tượng triển khai IDisposable trong một khối cuối cùng nếu bạn cần.

3

Tôi sử dụng để nghĩ về nó như là "Sử dụng nó mỗi khi loại thực hiện IDisposable và bạn không goinng cần trường hợp cụ thể này nữa".

1

Sử dụng sử dụng khi bạn cần xử lý đối tượng xác định. Ví dụ, nếu bạn mở một tập tin, tập tin sẽ bị khóa. Bạn sẽ thường xuyên muốn tập tin được đóng càng sớm càng tốt để các chương trình khác có thể truy cập nó. Nếu bạn không sử dụng sử dụng và viết smth như:

System.IO.FileStream writeStream = new System.IO.FileStream(fileName, System.IO.FileMode.OpenOrCreate)); 
System.IO.BinaryWriter writer = new System.IO.BinaryWriter(writeStream)); 
//do smth 

và một ngoại lệ xảy ra trong "làm smth" bạn không biết khi nào các đối tượng hoạt động trên một tập tin thực sự thanh lý khoản và tập tin được đóng lại.Với sử dụng bạn biết chắc chắn rằng khi bạn đã rời khỏi sử dụng khối tuyên bố - hoặc trực tiếp hoặc thông qua ngoại lệ đối tượng trong tuyên bố useing được xử lý bằng cách gọi IDisposable :: Vứt:

using(System.IO.FileStream writeStream = new System.IO.FileStream(fileName, System.IO.FileMode.OpenOrCreate)) { 
    using(System.IO.BinaryWriter writer = new System.IO.BinaryWriter(writeStream)) { 
     //do stmth 
    } 
} 
+0

Không, đối tượng * không được * hoàn thành. Nó chỉ được xử lý. Hoàn thiện và xử lý rất khác nhau. Thông thường việc xử lý sẽ triệt tiêu một finalizer (nếu có) nhưng chúng không giống nhau. –

+0

"Sử dụng khi bạn cần xử lý đối tượng xác định". Nó dường như với tôi rằng nếu một đối tượng thực hiện IDisposable bạn nên luôn luôn vứt bỏ nó nếu đối tượng là không cần thiết nữa. Bằng cách thực hiện IDisposable đối tượng nói "Tôi (có thể) sử dụng các tài nguyên cần được xử lý". –

+0

Điều gì sẽ xảy ra nếu việc thực hiện đối tượng trong câu hỏi thay đổi và xử lý cùng một lúc trở nên quan trọng? Lợi ích của việc không vứt bỏ ngay sau khi sử dụng là gì? –

1

Bạn có thể kèm theo một sử dụng bên trong một khối try/catch và bạn có thể kèm theo một khối try/catch bên trong một sử dụng.

Một tình huống mà sử dụng là tốt đẹp là khi bạn đang làm hoạt động cơ sở dữ liệu sử dụng DBConnection và DBCommands:

using (SqlConnection conn = new SqlConnection(connectionString)) 
using (SqlCommand command = new SqlCommand(sql, conn)) 
{ 
    // do something here 
} 

Bây giờ khi bạn rời khỏi khối sử dụng lệnh của bạn được xử lý và kết nối được đóng lại.

1

Điều Mitch nói là đúng. Vì vậy, việc sử dụng chính của việc sử dụng là để đảm bảo rằng các đối tượng IDisposable được xử lý, mà không cần phải mã một try/catch hoặc try/finally statement.

Giờ đây, bạn có thể thấy thú vị hơn khi sử dụng nâng cao hơn. Khi bạn sử dụng một câu lệnh using, trình biên dịch tạo ra một try/finally, và nó cũng tạo ra một lời gọi để Dispose() cho bạn bên trong cuối cùng mà nó tạo ra. Bạn có thể sử dụng phương thức Dispose() này như một "hook" để làm bất cứ điều gì bạn muốn ... không cần phải liên quan đến việc giải phóng tài nguyên.

Ví dụ: Jeffrey Richter sử dụng điều này trong đối tượng hẹn giờ mà anh đã viết. Bạn có thể làm một cái gì đó như thế này với nó (khái niệm chỉ):

using(var x = new Timer()) 
{ 
    // Do the operation you want timed 
} 

// Now, the timer has been stopped, because the 
// compiler-generated call to Dispose() has already 
// occurred, and Jeffrey uses Dispose() to stop 
// the timer. 
+0

Có vẻ hơi khó khăn một chút. Sau cú đúp đóng, bạn không có quyền truy cập vào x. Làm thế nào bạn có thể tận dụng lợi thế của bộ đếm thời gian đã dừng? –

+0

Vâng, đó là sự thật. Trong trường hợp bộ đếm thời gian của Richter, anh ta xuất ra thời gian đã trôi qua (và số lượng các bộ sưu tập rác đã xảy ra) cho Console. Vì vậy, ông không cần phải tham khảo các đối tượng sau khi đóng ngoặc. –

+0

Tuy nhiên, cũng có nhiều cách để đạt được điều đó nếu bạn muốn. Đối tượng có thể được yêu cầu kích hoạt một sự kiện hoặc kết quả địa điểm thành một bộ sưu tập. Hoặc bạn có thể tham số hóa đối tượng bằng một hành động lambda để thực hiện bên trong chính nó. Nhưng điều này đang nhận được quá xa câu hỏi ban đầu. –

2

Nếu bạn muốn biết làm thế nào để sáng tạo sử dụng nó trong các thiết kế của riêng bạn, xem xét thông qua một số mã của riêng bạn cho các tình huống mà một chút cụ thể của mã hoàn toàn phải thực hiện trước khi khối kèm theo được thoát. Đây là những tình huống trong đó try/finally hoặc using có thể giúp bạn.

Cụ thể, nếu bạn đã cố gắng đạt được điều này bằng cách bắt tất cả ngoại lệ, thì bạn phải thay đổi thành try/finally hoặc using thay thế.

Nếu mẫu xuất hiện nhiều lần, bạn có thể tạo một lớp thực hiện IDisposable để chụp mẫu và cho phép bạn gọi mẫu bằng câu lệnh using. Nhưng nếu bạn có một trường hợp cụ thể dường như là một lần, thì chỉ cần sử dụng try/finally.

Hai rất giống nhau, thực sự - using được quy định tại các điều khoản của try/finally, nhưng ngay cả nếu chúng ta chỉ có using, chúng tôi có thể xây dựng try/finally mình:

public class DisposeAnything : IDisposable 
{ 
    public Action Disposer; 

    public void Dispose() 
    { 
     Disposer(); 
    } 
} 

Bây giờ bạn có thể nói:

using (new DisposeAnything 
     { 
      Disposer =() => File.Delete(tempFileName) 
     }) 
{ 
    // blah 
} 

Mà là giống như:

try 
{ 
    // blah 
} 
finally 
{ 
    File.Delete(tempFileName); 
} 

Chỉ cần nghĩ về nó như là một cách để có được một số mã để thực thi khi rời khỏi một phạm vi.

+0

Hey, tham số hóa logic bỏ là một ý tưởng thú vị. Tôi tưởng tượng có nhiều cách để làm điều đó. Mát mẻ. –

+0

Đưa đến những điều trên cùng, không có nhiều điểm cho nó! Nhưng có thể có nhiều trường hợp nửa đường. Bạn có thể thấy cuộc thảo luận này thú vị: http://incrediblejourneysintotheknown.blogspot.com/2009/02/functional-replacement-for-using.html –

1

Nếu một lớp thực hiện IDisposable, đó có thể là lý do chính đáng. Vì vậy, bất kỳ lớp thực hiện IDisposable nên được xử lý.

1

Vì nó được gọi là "đường cú pháp" và sẽ tạo ra cùng một IL như một cấu trúc cố gắng/cuối cùng vứt bỏ, nó thực sự chỉ là một cách tốt để "rút ngắn" mã như vậy.

Tôi thích sử dụng nó để đơn giản hóa các phần mã nơi các đối tượng dùng một lần truy cập vào các tài nguyên như tệp và đối tượng đồ họa và tôi muốn đảm bảo rằng tôi không quên xử lý tài nguyên vật.

0

Tôi đã nhận thấy rằng khi phương pháp Vứt bỏ được biết là để thông báo, vì vậy các lập trình viên không bận tâm gọi nó là dường như vô nghĩa. Tuy nhiên, nếu đối tượng thực hiện IDisposable nó (hy vọng) cho một lý do và các phiên bản tương lai của đối tượng đó thực sự có thể có mã trong phương thức Vứt bỏ - vì vậy hãy luôn gọi nó.

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