Tôi có nên loại bỏ () Data set và DataTable không?


197

Cả Data set và DataTable đều triển khai IDis Dùng một lần, vì vậy, bằng các cách thực hành tốt nhất thông thường, tôi nên gọi các phương thức Dispose () của chúng.

Tuy nhiên, từ những gì tôi đã đọc cho đến nay, Dataset và DataTable thực sự không có bất kỳ tài nguyên không được quản lý nào, vì vậy, Dispose () thực sự không làm được gì nhiều.

Ngoài ra, tôi không thể chỉ sử dụng using(DataSet myDataSet...)vì Dataset có một bộ sưu tập DataTables.

Vì vậy, để an toàn, tôi cần phải lặp lại thông qua myDataSet.Tables, loại bỏ từng DataTables, sau đó loại bỏ Data set.

Vì vậy, có đáng để gặp rắc rối khi gọi Dispose () trên tất cả DataSets và DataTables của tôi không?

Phụ lục:

Đối với những người cho rằng nên xử lý Data set: Nói chung, mẫu xử lý là sử dụng usinghoặc try..finally, vì bạn muốn đảm bảo rằng Dispose () sẽ được gọi.

Tuy nhiên, điều này trở nên xấu thực sự nhanh chóng cho một bộ sưu tập. Ví dụ: bạn sẽ làm gì nếu một trong các lệnh gọi Dispose () ném ngoại lệ? Bạn có nuốt nó ("xấu") để bạn có thể tiếp tục loại bỏ phần tử tiếp theo không?

Hoặc, bạn có gợi ý rằng tôi chỉ gọi myDataSet.Dispose () và quên đi việc xử lý DataTables trong myDataSet.Tables không?


9
Vứt bỏ không phải là để ném bất kỳ ngoại lệ. Nếu nó không có văn bản thì nó không được viết tốt, vì vậy, hãy thử {some.Dispose (); } bắt {} là đủ. - blogs.msdn.com/b/clyon/archive/2004/09/23/233464.aspx
LukeSw

3
Tôi nhận thấy rò rỉ bộ nhớ rõ ràng trong một trong những ứng dụng của tôi sử dụng nhiều đối tượng Dataset. Tôi đã không gọi .Dispose () hoặc sử dụng các khối "sử dụng" cho các đối tượng đó. Vì vậy, tôi đã duyệt mã và thêm một khối "sử dụng" vào mọi nơi tôi đang tạo một Dataset hoặc DataTable và voila bộ nhớ hiện được phát hành. Dường như với tôi một dấu hiệu chắc chắn rằng trên thực tế .Dispose () là cần thiết cho Dataset và DataTable.
chóng mặt.stackoverflow

Câu trả lời:


147

Dưới đây là một vài cuộc thảo luận giải thích tại sao Vứt bỏ không cần thiết cho một Bộ dữ liệu.

Vứt bỏ hay không vứt bỏ? :

Phương thức Vứt bỏ trong Bộ dữ liệu tồn tại CHỈ vì tác dụng phụ của kế thừa-- nói cách khác, nó thực sự không làm được gì hữu ích trong việc hoàn thiện.

Nên vứt bỏ trên các đối tượng DataTable và Dataset? bao gồm một số lời giải thích từ một MVP:

Không gian tên system.data (ADONET) không chứa tài nguyên không được quản lý. Do đó, không cần phải loại bỏ bất kỳ thứ gì miễn là bạn chưa thêm cho mình thứ gì đặc biệt.

Hiểu phương pháp Vứt bỏ và bộ dữ liệu? có một nhận xét từ chính quyền Scott Allen:

Trong pratice, chúng tôi hiếm khi loại bỏ một Bộ dữ liệu vì nó mang lại rất ít lợi ích "

Vì vậy, sự đồng thuận ở đây là hiện tại không có lý do chính đáng để gọi Vứt bỏ trên một Bộ dữ liệu.


7
Các liên kết được cung cấp hoàn toàn bỏ lỡ điểm DataTable là một loại đối tượng Hoàn thiện. Xin vui lòng xem câu trả lời của Nariman dưới đây.
Herman

Câu trả lời thú vị nhưng những gì về SqlConnection, SqlCommand và SqlDataAd CHƯƠNG, thì Dispose có nên được gọi một cách rõ ràng không?
Willy

@Willy Tôi nghĩ rằng rất nhiều người sử dụng một câu lệnh sử dụng cho IDisposeables. bằng cách sử dụng (SqlConnection cn = new SqlConnection (ConnectionString)) {bằng cách sử dụng (SqlCommand cm = new SqlCommand (lệnhString, cn)) {cn.Open (); cm.ExecuteNonQuery (); }}
DOK

1
@Willy vâng, những thứ đó hoàn toàn nên được xử lý vì chúng sử dụng các tài nguyên không được quản lý. Cho dù nó được gọi rõ ràng hay ngầm sử dụng một usingkhối là tùy thuộc vào bạn.
D Stanley

129

Cập nhật (ngày 1 tháng 12 năm 2009):

Tôi muốn sửa đổi câu trả lời này và thừa nhận rằng câu trả lời ban đầu là thiếu sót.

Các phân tích ban đầu không áp dụng đối với các đối tượng yêu cầu kết thúc - và thời điểm đó thực hành không nên được chấp nhận trên bề mặt mà không có một chính xác, sâu sự hiểu biết vẫn đứng.

Tuy nhiên, hóa ra DataSets, DataViews, DataTables ngăn chặn quyết toán trong các hàm tạo của chúng - đây là lý do tại sao việc gọi Dispose () trên chúng rõ ràng không làm gì cả.

Có lẽ, điều này xảy ra bởi vì họ không có tài nguyên không được quản lý; Vì vậy, mặc dù thực tế là MarshalByValueComponent tạo ra các khoản phụ cấp cho các tài nguyên không được quản lý, những triển khai cụ thể này không có nhu cầu và do đó có thể từ bỏ quyết toán.

(Các tác giả .NET đó sẽ quan tâm đến việc triệt tiêu quyết toán đối với chính các loại thường chiếm nhiều bộ nhớ nhất nói lên tầm quan trọng của thực tiễn này nói chung đối với các loại hoàn thiện.)

Mặc dù vậy, các chi tiết này vẫn chưa được ghi chép đầy đủ kể từ khi .NET Framework ra đời (gần 8 năm trước) khá đáng ngạc nhiên (về cơ bản bạn để các thiết bị của mình để sàng lọc mặc dù các vật liệu mơ hồ, mâu thuẫn để đặt các mảnh ghép lại với nhau đôi khi làm nản lòng nhưng không cung cấp sự hiểu biết đầy đủ hơn về khuôn khổ mà chúng ta dựa vào hàng ngày).

Sau khi đọc rất nhiều, đây là sự hiểu biết của tôi:

Nếu một đối tượng yêu cầu hoàn thiện, nó có thể chiếm bộ nhớ lâu hơn mức cần thiết - đây là lý do: a) Bất kỳ loại nào xác định hàm hủy (hoặc kế thừa từ một loại xác định hàm hủy) đều được coi là hoàn thiện; b) Khi cấp phát (trước khi hàm tạo chạy), một con trỏ được đặt trên hàng đợi Hoàn thiện; c) Một đối tượng có thể hoàn thiện thường yêu cầu thu hồi 2 bộ sưu tập (thay vì tiêu chuẩn 1); d) Việc loại bỏ quyết toán không loại bỏ một đối tượng khỏi hàng đợi quyết toán (như được báo cáo bởi! FinalizeQueue trong SOS) Lệnh này gây hiểu nhầm; Biết những đối tượng nào trong hàng đợi quyết toán (trong và chính nó) không hữu ích; Biết những đối tượng nào trong hàng đợi quyết toán và vẫn yêu cầu hoàn thiện sẽ hữu ích (có lệnh nào cho việc này không?)

Việc loại bỏ hoàn thiện sẽ tắt một chút trong tiêu đề của đối tượng cho biết thời gian chạy rằng nó không cần phải có Trình hoàn thiện của nó được gọi (không cần phải di chuyển hàng đợi FReachable); Nó vẫn còn trên hàng đợi Quyết toán (và tiếp tục được báo cáo bởi! FinalizeQueue trong SOS)

Các lớp DataTable, Dataset, DataView đều được bắt nguồn từ MarshalByValueComponent, một đối tượng hoàn thiện có thể (có khả năng) xử lý các tài nguyên không được quản lý

  • Vì DataTable, Dataset, DataView không giới thiệu các tài nguyên không được quản lý, chúng ngăn chặn việc hoàn thiện trong các hàm tạo của chúng
  • Mặc dù đây là một mẫu không bình thường, nhưng nó giúp người gọi không phải lo lắng về việc gọi Vứt bỏ sau khi sử dụng
  • Điều này và thực tế là DataTables có thể có khả năng được chia sẻ trên các DataSets khác nhau, có khả năng là lý do tại sao DataSets không quan tâm đến việc xử lý DataTables con
  • Điều này cũng có nghĩa là các đối tượng này sẽ xuất hiện dưới! FinalizeQueue trong SOS
  • Tuy nhiên, những đối tượng này vẫn có thể được thu hồi sau một bộ sưu tập duy nhất, như các đối tác không thể hoàn thiện của chúng

4 (tài liệu tham khảo mới):

Câu trả lời gốc:

Có rất nhiều câu trả lời sai lệch và nói chung rất kém về điều này - bất kỳ ai hạ cánh ở đây nên bỏ qua tiếng ồn và đọc các tài liệu tham khảo dưới đây một cách cẩn thận.

Không còn nghi ngờ gì nữa, Vứt bỏ nên được gọi trên bất kỳ đối tượng Hoàn thiện nào.

DataTables có thể hoàn thành .

Gọi Vứt bỏ đáng kể tăng tốc độ lấy lại bộ nhớ.

MarshalByValueComponent gọi GC.SuppressFinalize (this) trong Dispose () - bỏ qua điều này có nghĩa là phải chờ hàng chục nếu không phải hàng trăm bộ sưu tập Gen0 trước khi bộ nhớ được lấy lại:

Với sự hiểu biết cơ bản về hoàn thiện này, chúng ta đã có thể suy ra một số điều rất quan trọng:

Đầu tiên, các đối tượng cần hoàn thiện sẽ sống lâu hơn các đối tượng không. Trên thực tế, họ có thể sống lâu hơn rất nhiều. Chẳng hạn, giả sử một đối tượng nằm trong gen2 cần được hoàn thiện. Hoàn thiện sẽ được lên lịch nhưng đối tượng vẫn ở gen2, vì vậy nó sẽ không được thu thập lại cho đến khi bộ sưu tập gen2 tiếp theo xảy ra. Đó thực sự có thể là một thời gian rất dài, và trên thực tế, nếu mọi thứ đang diễn ra tốt đẹp thì đó sẽ là một thời gian dài, bởi vì các bộ sưu tập gen2 rất tốn kém và do đó chúng tôi muốn chúng xảy ra rất ít khi xảy ra. Các đối tượng cũ hơn cần hoàn thiện có thể phải chờ hàng chục nếu không phải là hàng trăm bộ sưu tập gen0 trước khi không gian của chúng được lấy lại.

Thứ hai, các đối tượng cần hoàn thiện gây ra thiệt hại tài sản thế chấp. Do các con trỏ đối tượng bên trong phải còn hiệu lực, nên không chỉ các đối tượng cần trực tiếp hoàn thiện sẽ lưu lại trong bộ nhớ mà tất cả mọi thứ mà đối tượng đề cập, trực tiếp và gián tiếp, cũng sẽ vẫn còn trong bộ nhớ. Nếu một cây khổng lồ của các vật thể được neo bởi một vật thể cần phải hoàn thiện, thì toàn bộ cây sẽ tồn tại, có khả năng trong một thời gian dài như chúng ta vừa thảo luận. Do đó, điều quan trọng là sử dụng các công cụ hoàn thiện một cách tiết kiệm và đặt chúng trên các đối tượng có càng ít con trỏ đối tượng bên trong càng tốt. Trong ví dụ về cây tôi vừa đưa ra, bạn có thể dễ dàng tránh được vấn đề bằng cách di chuyển các tài nguyên cần hoàn thiện sang một đối tượng riêng biệt và giữ một tham chiếu đến đối tượng đó trong thư mục gốc của cây.

Cuối cùng, các đối tượng cần hoàn thiện tạo công việc cho luồng hoàn thiện. Nếu quá trình hoàn thiện của bạn là một quá trình phức tạp, thì chuỗi xử lý một và duy nhất sẽ dành nhiều thời gian để thực hiện các bước đó, điều này có thể gây ra tồn đọng công việc và do đó khiến nhiều đối tượng phải nán lại chờ đợi quyết toán. Do đó, điều cực kỳ quan trọng là người hoàn thiện làm càng ít việc càng tốt. Cũng cần nhớ rằng mặc dù tất cả các con trỏ đối tượng vẫn còn hiệu lực trong quá trình hoàn thiện, nhưng có thể xảy ra trường hợp các con trỏ đó dẫn đến các đối tượng đã được hoàn thiện và do đó có thể ít hữu ích hơn. Nói chung là an toàn nhất để tránh theo dõi các con trỏ đối tượng trong mã quyết toán mặc dù các con trỏ là hợp lệ. Một đường dẫn mã quyết toán an toàn, ngắn là tốt nhất.

Lấy nó từ một người đã xem 100 MB MB DataTables không được tham chiếu trong Gen2: điều này cực kỳ quan trọng và hoàn toàn bị bỏ lỡ bởi các câu trả lời trên chuỗi này.

Người giới thiệu:

1 - http://msdn.microsoft.com/en-us/l Library / ms973837.aspx

2 - http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage -collector-Performance-using-Finalizedispose-pattern.aspx

3 - http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Quản lý /


Điểm tốt. Làm thế nào để bạn thường cấu trúc mã của mình khi bạn có một Bộ dữ liệu với nhiều Dữ liệu? Tấn của lồng sử dụng báo cáo? Một lần thử .. cuối cùng để làm sạch tất cả cùng một lúc?
mockish

14
Tuyên bố "Tuy nhiên, hóa ra DataSets, DataViews, DataTables ngăn chặn quyết toán trong các hàm tạo của chúng - đây là lý do tại sao gọi Dipose () trên chúng rõ ràng không làm gì cả." là một phi sequitur: hai khái niệm chủ yếu không liên quan; một cái gì đó ngăn chặn quyết toán vẫn có thể làm một cái gì đó trong Dispose (). Thật vậy, nó thực sự có ý nghĩa hơn nếu chúng ta đảo ngược nó: Vứt bỏ () không làm gì cả, đó là lý do tại sao nó ngăn chặn sự hoàn thiện trong hàm tạo, tức là vì sẽ không có gì để làm, nó không muốn làm phiền đến việc gọi cho bộ hoàn thiện ( mà thường gọi là xử lý).
Marc Gravell

Cảm ơn. Liệu cuộc thảo luận này có áp dụng cho TableAdapters không?
Bondolin


24

Bạn nên cho rằng nó làm một cái gì đó hữu ích và gọi Vứt bỏ ngay cả khi nó không làm gì trong hiện tại. Các hiện thân của NET Framework, không có gì đảm bảo nó sẽ giữ nguyên như vậy trong các phiên bản trong tương lai dẫn đến việc sử dụng tài nguyên không hiệu quả.


Không có gì đảm bảo rằng nó sẽ triển khai IDis Dùng trong tương lai. Tôi sẽ đồng ý với bạn nếu nó đơn giản như việc sử dụng (...), nhưng trong trường hợp của Dataset, có vẻ như rất nhiều rắc rối chẳng vì gì cả.
maysish

28
Nó khá an toàn để giả định rằng nó sẽ luôn luôn thực hiện IDis Dùng. Thêm hoặc xóa giao diện là một thay đổi đột phá, trong khi thay đổi việc thực hiện Dispose thì không.
Greg Dean

5
Ngoài ra, một nhà cung cấp khác có thể có một triển khai thực sự làm điều gì đó với IDis Dùng.
Matt Spradley

Không đề cập đến việc DataTablekhông được niêm phong - không phải là vấn đề lớn khi bạn thực hiện new DataTable, nhưng khá quan trọng khi lấy DataTablelàm đối số hoặc kết quả của một cuộc gọi phương thức.
Luaan

17

Ngay cả khi đối tượng không có tài nguyên không được quản lý, việc xử lý có thể giúp đỡ GC bằng cách phá vỡ các biểu đồ đối tượng. Nói chung, nếu đối tượng thực hiện IDis Dùng thì nên gọi Dispose ().

Việc Dispose () có thực sự làm điều gì đó hay không phụ thuộc vào lớp đã cho. Trong trường hợp của Dataset, việc triển khai Dispose () được kế thừa từ MarshalByValueComponent. Nó loại bỏ chính nó khỏi container và gọi sự kiện Disposed. Mã nguồn bên dưới (được phân tách bằng .NET Reflector):

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this)
        {
            if ((this.site != null) && (this.site.Container != null))
            {
                this.site.Container.Remove(this);
            }
            if (this.events != null)
            {
                EventHandler handler = (EventHandler) this.events[EventDisposed];
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            }
        }
    }
}

1
Thật. Tôi đã thấy một số mã gần đây khi có rất nhiều DataTables được tạo trong một vòng lặp rất lớn mà không bị loại bỏ. Điều này dẫn đến tất cả bộ nhớ bị tiêu tốn trên máy tính và quá trình bị sập khi hết bộ nhớ. Sau khi tôi nói với nhà phát triển gọi xử lý trên DataTable, vấn đề đã biến mất.
RichardOD

7

Bạn có tự tạo DataTables không? Bởi vì việc lặp đi lặp lại qua con cái của bất kỳ Đối tượng nào (như trong Dataset.Tables) thường không cần thiết, vì đó là công việc của Cha mẹ để loại bỏ tất cả các thành viên con của nó.

Nói chung, quy tắc là: Nếu bạn đã tạo nó và nó thực hiện IDis Dùng một lần, hãy loại bỏ nó. Nếu bạn KHÔNG tạo ra nó, thì KHÔNG loại bỏ nó, đó là công việc của đối tượng cha. Nhưng mỗi đối tượng có thể có các quy tắc đặc biệt, hãy kiểm tra Tài liệu.

Đối với .net 3.5, nó nói rõ ràng "Loại bỏ nó khi không sử dụng nữa", vì vậy đó là những gì tôi sẽ làm.


4
Từ những gì tôi hiểu, sự đồng thuận chung là một đối tượng nên loại bỏ các tài nguyên không được quản lý của chính nó. Tuy nhiên, một bộ sưu tập của các đối tượng IDisposable sẽ không lặp chung thông qua các yếu tố của nó để xử lý mỗi người, bởi vì có thể có tài liệu tham khảo khác để yếu tố của nó bên ngoài của bộ sưu tập: stackoverflow.com/questions/496722/...
mbeckish

1
Đúng, Bộ sưu tập luôn là thứ tôi coi là đặc biệt vì chúng thường không "làm" bất cứ thứ gì, chúng chỉ đơn thuần là ... Container, vì vậy tôi không bao giờ bận tâm về điều đó.
Michael Stum

7

Tôi gọi xử lý bất cứ lúc nào một đối tượng thực hiện IDisposeizable. Đó là vì một lý do.

DataSets có thể là bộ nhớ lớn. Càng sớm được đánh dấu để dọn dẹp, càng tốt.

cập nhật

Đã 5 năm kể từ khi tôi trả lời câu hỏi này. Tôi vẫn đồng ý với câu trả lời của tôi. Nếu có một phương thức xử lý, nó sẽ được gọi khi bạn hoàn thành với đối tượng. Giao diện IDispose được triển khai vì một lý do.


5
Gọi vứt bỏ không tăng tốc việc lấy lại bộ nhớ, để làm điều đó bạn sẽ phải tự khởi động trình thu gom rác, đây thường là một kế hoạch tồi.
Tetraneutron

2
Nếu Dispose đặt một loạt các tham chiếu thành null, nó có thể khiến các đối tượng trở thành ứng cử viên cho bộ sưu tập có thể bị bỏ qua.
Greg Dean

1
Quan điểm của Vứt bỏ là không dọn sạch bộ nhớ của các đối tượng được quản lý - đó là công việc của người thu gom rác. Vấn đề là làm sạch các đối tượng không được quản lý. Dường như có bằng chứng cho thấy DataSets không có bất kỳ tài liệu tham khảo nào không được quản lý nên về mặt lý thuyết không cần phải xử lý chúng. Điều đó đã được nói, tôi chưa bao giờ ở trong một tình huống mà tôi phải đi ra ngoài để gọi Vứt bỏ - dù sao tôi cũng sẽ gọi nó.
cbp

4
Việc sử dụng chính của IDis Dùng là giải phóng các tài nguyên không được quản lý. Thông thường, nó cũng sửa đổi trạng thái theo cách có ý nghĩa đối với một trường hợp xử lý. (tức là các thuộc tính được đặt thành false, các tham chiếu được đặt thành null, v.v.)
Greg Dean

3
Nếu có một phương pháp xử lý trên một đối tượng, nó được đặt ở đó vì một lý do bất kể đó có phải là để làm sạch các đối tượng không được quản lý hay không.
Chuck Conway

4

Nếu ý định của bạn hoặc bối cảnh của câu hỏi này thực sự là bộ sưu tập rác, thì bạn có thể đặt bộ dữ liệu và dữ liệu thành null hoặc sử dụng từ khóa bằng cách sử dụng và để chúng ra khỏi phạm vi. Vứt bỏ không làm gì nhiều như Tetraneutron đã nói trước đó. GC sẽ thu thập các đối tượng dữ liệu không còn được tham chiếu và cả các đối tượng nằm ngoài phạm vi.

Tôi thực sự muốn SO buộc mọi người bỏ phiếu để thực sự viết bình luận trước khi hạ thấp câu trả lời.


+ 1 Tôi đoán một số người không muốn cho phép người khác xem xét các quan điểm khác nhau.
DOK

2
bỏ phiếu, không có cách nào không cho phép mọi người xem xét các quan điểm khác nhau.
Greg Dean

1

Bộ dữ liệu triển khai IDis Dùng kỹ MarshalByValueComponent, thực hiện IDis Dùng một lần. Vì các bộ dữ liệu được quản lý, không có lợi ích thực sự nào để gọi xử lý.


6
Nó có thể bây giờ, ai biết nó sẽ làm gì sau này.
Greg Dean

Thái độ này trong đó bạn suy đoán rằng bất kỳ mã nào trong tương lai sẽ không làm những gì nó được cho là một nỗi đau trong giả định cho tất cả những người liên quan.
microsOnOnD

0

Hãy thử sử dụng hàm Clear (). Nó hoạt động tuyệt vời cho tôi để xử lý.

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();

0

Không cần vứt bỏ () vì Bộ dữ liệu kế thừa lớp MarshalByValueComponent và lớp MarshalByValueComponent thực hiện Giao diện IDis Dùng một lần

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.