2013-08-29 43 views
8

Vì vậy, tôi chỉ đọc về mô hình RAII cho thu gom rác ngôn ngữ không, và điều này section bắt mắt tôi:Tại sao mô hình Vứt bỏ trong C# không hoạt động giống như RAII trong C++

giới hạn này thường gặp bất cứ khi nào phát triển các lớp tùy chỉnh. Các lớp tùy chỉnh trong C# và Java phải thực hiện rõ ràng phương thức vứt bỏ để được xử lý tương thích với mã máy khách. Phương thức vứt bỏ phải chứa tất cả các tài nguyên con của lớp. Giới hạn này không tồn tại trong C++ với RAII, nơi mà các destructor của các lớp tùy chỉnh tự động hủy tất cả các tài nguyên con đệ quy mà không yêu cầu bất kỳ mã rõ ràng nào.

Tại sao C++ có thể theo dõi chính xác các tài nguyên này được phân bổ trong mẫu RAII, nhưng chúng tôi không nhận được Stack Unwinding đáng yêu này với cấu trúc C# bằng cấu trúc?

+0

Đọc liên quan: http://blogs.msdn.com/b/brada/archive/2005/02/11/371015.aspx – delnan

+0

Bạn biết bao nhiêu năm auto_ptr, shared_ptr và những thứ tương tự cần thiết cho C++ có thể sử dụng chính xác thành ngữ RAII với các bộ sưu tập không? :-) – xanatos

+0

@xanatos bạn muốn nói, cộng đồng C++ mất bao nhiêu năm để nắm lấy điều đó? Bởi vì những thứ đó đã có trong ngôn ngữ tất cả cùng. –

Trả lời

10

Giả sử đối tượng O bao gồm hai đối tượng sở hữu tài nguyên R và S. Điều gì sẽ xảy ra nếu O bị hủy?

Trong C++ với RAII, các đối tượng có thể sở hữu các đối tượng khác sao cho phá hủy của một đối tượng nhất thiết phải được ghép nối với nhau. Nếu O sở hữu R và S - bằng cách lưu trữ chúng theo giá trị hoặc bằng cách sở hữu thứ gì đó sở hữu R và S (unique_ptr, vùng chứa lưu trữ R và S theo giá trị), thì tiêu hủy O nhất thiết phải phá hủy R và S. Miễn là các destructors của R và S làm sạch đúng cách sau khi mình, O không cần phải làm bất cứ điều gì bằng tay.

Ngược lại, các đối tượng C# không có chủ sở hữu quyết định thời điểm kết thúc cuộc đời. Ngay cả khi O sẽ bị phá hủy một cách xác định (nói chung là không), R và S có thể được truy cập bằng một tham chiếu khác. Hơn nữa, cách O đề cập đến R và S cũng giống như bất kỳ biến cục bộ nào khác, trường đối tượng, phần tử mảng, v.v. đề cập đến R và S. Nói cách khác, không có cách nào để biểu thị quyền sở hữu, vì vậy máy tính có thể ' t quyết định khi đối tượng là giả định bị hủy và khi đó chỉ là tham chiếu không sở hữu/mượn. Chắc chắn bạn sẽ không mong đợi mã này để đóng tập tin?

File f = GetAFile(); 
return f; // end of method, the reference f disappears 

Nhưng theo như CLR có liên quan, tham chiếu từ địa phương f ở đây chính xác giống như tham chiếu từ O đến R/S.

TL; DR Quyền sở hữu.

+1

Giả sử rằng ngôn ngữ bạn đã viết đoạn mã hỗ trợ di chuyển ngữ nghĩa, câu lệnh 'return f' không được vứt bỏ tệp. Trong thực tế, trong C++/CLI, điều này sẽ làm việc tốt và làm những gì mong đợi. Nếu bạn không trả lại 'f' hoặc chuyển nó ra khỏi hàm, nó sẽ được xử lý đúng cách. Về cơ bản nó làm những gì bạn mong đợi nó nên làm, với ngữ nghĩa của C++. –

+0

@KubaOber Nhưng C# không, và nó thiếu một số điều kiện tiên quyết để thêm một mô hình sở hữu lành mạnh. Đó chính là điểm của câu trả lời của tôi. – delnan

+0

TL; Ngữ nghĩa giá trị DR – Dejavu

3

Trong C++, các đối tượng có thời gian tồn tại nhất định. Thời gian tồn tại của các biến tự động kết thúc khi chúng nằm ngoài phạm vi và thời gian tồn tại của các đối tượng được phân bổ động sẽ kết thúc khi chúng bị xóa.

Trong C#, hầu hết các đối tượng được phân bổ động và không xóa. Vì vậy, các đối tượng không có một điểm xác định trong thời gian khi chúng được "xóa". Điều gần nhất bạn có, là using.

+1

Đó không phải là những gì câu hỏi là về. Câu hỏi đặt ra là về thành phần của các đối tượng quản lý tài nguyên trong một đối tượng duy nhất. Trong C++, đối tượng soạn thảo giải phóng các tài nguyên tự động mà không có mã, trong C# bạn phải giải phóng chúng một cách rõ ràng trong 'Vứt bỏ'. – Alex

+1

@ Alex Công bằng đủ, đó là một tiền thưởng lớn C + + có. :-D –

2

Câu trả lời ngắn gọn: Lần cuối bạn dọn sạch phân bổ bộ nhớ của riêng bạn/phân bổ tài nguyên trong trình phá hủy Java/C# ở đâu? Trong thực tế, khi nào là lần cuối bạn nhớ viết một Java/C# destructor?

Vì bạn chịu trách nhiệm tự dọn dẹp sau khi bạn ở trong C++, bạn phải làm việc dọn dẹp. Vì vậy, khi bạn ngừng sử dụng một tài nguyên (nếu bạn đã viết mã chất lượng tốt), nó sẽ ngay lập tức được làm sạch.

Trong ngôn ngữ được quản lý, bộ thu gom rác chịu trách nhiệm thực hiện việc dọn dẹp. Một nguồn tài nguyên phân bổ của bạn vẫn có thể ở đó lâu sau khi bạn ngừng sử dụng nó (nếu bộ thu gom rác được triển khai kém). Đây là vấn đề khi các đối tượng được quản lý tạo tài nguyên không được quản lý (ví dụ: kết nối cơ sở dữ liệu). Đó là lý do tại sao các phương pháp Dispose tồn tại - để nói cho những tài nguyên không được quản lý đó biến mất. Kể từ khi destructor không nhận được gọi cho đến khi thu gom rác dọn dẹp bộ nhớ, làm sạch sẽ có vẫn còn để lại (hữu hạn) tài nguyên mở cho lâu hơn nó cần thiết được.

2

Bởi vì để thực hiện điều này một cách chính xác trong C#, bạn cần phải bằng cách nào đó đánh dấu đối tượng nào lớp sở hữu và đối tượng nào được chia sẻ. Có lẽ một cú pháp tương tự như thế này:

// NOT VALID CODE 
public class Manager: IDisposable 
{ 
    // Tell the runtime to call resource.Dispose when disposing Manager 
    using private UnmanagedResource resource; 
} 

tôi đoán là họ đã quyết định không đi theo con đường đó bởi vì nếu bạn phải đánh dấu các đối tượng bạn sở hữu, bạn phải viết mã về nó. Và nếu bạn phải viết mã về nó, bạn chỉ có thể viết mã theo phương thức Dispose, gọi số Dispose cho các đối tượng bạn sở hữu :)

Trong C++, quyền sở hữu đối tượng thường rất rõ ràng - nếu bạn giữ chính trường hợp đó, bạn sở hữu nó. Trong C# bạn không bao giờ giữ chính thể hiện, bạn luôn giữ một tham chiếu và tham chiếu có thể là một cái gì đó mà bạn sở hữu hoặc một cái gì đó bạn sử dụng - không có cách nào để cho biết điều gì là đúng cho một trường hợp cụ thể.

+0

Trong ngôn ngữ/khung được thiết kế phù hợp, đánh dấu các đối tượng mà một lớp sở hữu sẽ * tránh * cần phải viết mã để làm sạch các đối tượng đó.Chú giải trường sẽ không loại bỏ sự cần thiết phải viết mã dọn dẹp trong tất cả các trường hợp, nhưng sẽ chăm sóc rất nhiều trong số chúng. Thật không may, làm cho mọi thứ hoạt động đúng cách sẽ yêu cầu một phương tiện yêu cầu một phương thức lớp cụ thể nên được ném nếu một hàm tạo ném một ngoại lệ. Nếu không có khả năng đó, sẽ không có cách nào cho một lớp cơ sở để tránh rò rỉ tài nguyên nếu một hàm tạo lớp dẫn xuất ném. – supercat