CẬP NHẬT : Tôi đã sử dụng câu hỏi này làm cơ sở cho một bài báo có thể tìm thấy ở đây ; xem nó để thảo luận thêm về vấn đề này. Cảm ơn vì câu hỏi hay!
Mặc dù câu trả lời của Schabse tất nhiên là đúng và trả lời câu hỏi đã được hỏi, nhưng có một biến thể quan trọng về câu hỏi của bạn mà bạn chưa hỏi:
Điều gì xảy ra nếu font4 = new Font()
ném sau khi tài nguyên không được quản lý được cấp phát bởi hàm tạo nhưng trước khi ctor trả về và điền vàofont4
tham chiếu?
Hãy để tôi nói rõ hơn một chút. Giả sử chúng ta có:
public sealed class Foo : IDisposable
{
private int handle = 0;
private bool disposed = false;
public Foo()
{
Blah1();
int x = AllocateResource();
Blah2();
this.handle = x;
Blah3();
}
~Foo()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
if (this.handle != 0)
DeallocateResource(this.handle);
this.handle = 0;
this.disposed = true;
}
}
}
Bây giờ chúng tôi có
using(Foo foo = new Foo())
Whatever(foo);
Điều này giống như
{
Foo foo = new Foo();
try
{
Whatever(foo);
}
finally
{
IDisposable d = foo as IDisposable;
if (d != null)
d.Dispose();
}
}
ĐỒNG Ý. Giả sử Whatever
ném. Sau đófinally
khối chạy và tài nguyên được phân bổ. Không vấn đề gì.
Giả sử Blah1()
ném. Sau đó, ném xảy ra trước khi tài nguyên được cấp phát. Đối tượng đã được cấp phát nhưng ctor không bao giờ trả về, vì vậy foo
không bao giờ được điền vào. Chúng tôi không bao giờ nhập vào try
vì vậy chúng tôi không bao giờ nhập một finally
trong hai. Tham chiếu đối tượng đã bị mất. Cuối cùng, GC sẽ phát hiện ra điều đó và đưa nó vào hàng đợi người hoàn thiện. handle
vẫn là 0, vì vậy trình hoàn thiện không làm gì cả. Lưu ý rằng trình hoàn thiện được yêu cầu phải mạnh mẽ khi đối mặt với một đối tượng đang được hoàn thiện mà hàm tạo không bao giờ hoàn thành . Bạn được yêu cầu viết các bản hoàn thiện thật mạnh mẽ. Đây là một lý do khác tại sao bạn nên giao việc viết các bản hoàn thiện cho các chuyên gia chứ không phải cố gắng tự làm.
Giả sử Blah3()
ném. Việc ném xảy ra sau khi tài nguyên được cấp phát. Nhưng một lần nữa, foo
không bao giờ được điền vào, chúng tôi không bao giờ nhậpfinally
, và đối tượng được làm sạch bởi chuỗi hoàn thiện. Lần này tay cầm khác 0 và trình hoàn thiện sẽ làm sạch nó. Một lần nữa, trình hoàn thiện đang chạy trên một đối tượng mà hàm tạo không bao giờ thành công, nhưng trình hoàn thiện vẫn chạy. Rõ ràng là phải làm vì thời gian này, nó có việc phải làm.
Bây giờ, giả sử Blah2()
ném. Việc ném xảy ra sau khi tài nguyên được cấp phát nhưng trước khi handle
được điền vào! Một lần nữa, trình hoàn thiện sẽ chạy nhưng bây giờ handle
vẫn là 0 và chúng tôi bị rò rỉ phần xử lý!
Bạn cần phải viết mã cực kỳ thông minh để ngăn chặn rò rỉ này xảy ra. Bây giờ, trong trường hợp Font
tài nguyên của bạn , ai là người quan tâm? Chúng tôi bị rò rỉ một cái xử lý phông chữ, chuyện lớn. Nhưng nếu bạn hoàn toàn tích cực yêu cầu rằng mọi tài nguyên không được quản lý phải được dọn dẹp bất kể thời gian của các trường hợp ngoại lệ là gì sau đó bạn có một vấn đề rất khó khăn trên tay của bạn.
CLR phải giải quyết vấn đề này với ổ khóa. Kể từ C # 4, các khóa sử dụng lock
câu lệnh đã được triển khai như sau:
bool lockEntered = false;
object lockObject = whatever;
try
{
Monitor.Enter(lockObject, ref lockEntered);
lock body here
}
finally
{
if (lockEntered) Monitor.Exit(lockObject);
}
Enter
đã được viết rất cẩn thận để không có vấn đề gì ngoại lệ được ném ra , lockEntered
được đặt thành true nếu và chỉ khi khóa thực sự được thực hiện. Nếu bạn có yêu cầu tương tự thì những gì bạn cần thực sự là viết:
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
và viết AllocateResource
một cách khéo léo Monitor.Enter
sao cho không có vấn đề gì xảy ra bên trong AllocateResource
, dấu handle
được điền vào nếu và chỉ khi nó cần được phân bổ.
Mô tả các kỹ thuật để làm như vậy nằm ngoài phạm vi của câu trả lời này. Tham khảo ý kiến chuyên gia nếu bạn có yêu cầu này.