Đặt đối tượng thành Null / Không có gì sau khi sử dụng trong .NET


187

Bạn có nên đặt tất cả các đối tượng thành null( Nothingtrong VB.NET) sau khi hoàn thành chúng không?

Tôi hiểu rằng trong .NET, điều cần thiết là loại bỏ bất kỳ trường hợp nào của các đối tượng triển khai IDisposablegiao diện để giải phóng một số tài nguyên mặc dù đối tượng vẫn có thể là thứ gì đó sau khi được xử lý (do đó thuộc isDisposedtính ở dạng), vì vậy tôi cho rằng nó vẫn có thể cư trú trong bộ nhớ hoặc ít nhất là một phần?

Tôi cũng biết rằng khi một đối tượng vượt ra khỏi phạm vi thì nó sẽ được đánh dấu để thu thập sẵn sàng cho lần chuyển tiếp theo của trình thu gom rác (mặc dù điều này có thể mất thời gian).

Vì vậy, với suy nghĩ này sẽ thiết lập nó để nulltăng tốc hệ thống giải phóng bộ nhớ vì nó không phải làm việc rằng nó không còn trong phạm vi và chúng có bất kỳ tác dụng phụ xấu nào không?

Các bài viết MSDN không bao giờ làm điều này trong các ví dụ và hiện tại tôi làm điều này vì tôi không thể thấy tác hại. Tuy nhiên tôi đã gặp phải một loạt các ý kiến ​​vì vậy bất kỳ ý kiến ​​đều hữu ích.


4
+1 câu hỏi tuyệt vời. Có ai biết một tình huống mà trình biên dịch sẽ tối ưu hóa hoàn toàn việc chuyển nhượng không? tức là có ai đã xem MSIL trong các trường hợp khác nhau và lưu ý IL đã đặt đối tượng thành null (hoặc thiếu đối tượng).
Tim Medora

Câu trả lời:


73

Karl hoàn toàn chính xác, không cần thiết phải đặt đối tượng thành null sau khi sử dụng. Nếu một đối tượng thực hiện IDisposable, chỉ cần đảm bảo bạn gọi IDisposable.Dispose()khi bạn hoàn thành với đối tượng đó (được bao bọc trong một try.. finally, hoặc, một using()khối). Nhưng ngay cả khi bạn không nhớ gọi Dispose(), phương thức hoàn thiện trên đối tượng sẽ gọi Dispose()cho bạn.

Tôi nghĩ rằng đây là một điều trị tốt:

Đào sâu vào IDis Dùng

và điều này

Hiểu IDis Dùng

Không có bất kỳ điểm nào trong việc cố gắng đoán thứ hai về GC và các chiến lược quản lý của nó bởi vì nó tự điều chỉnh và mờ đục. Có một cuộc thảo luận tốt về hoạt động bên trong với Jeffrey Richter trên Dot Net Rocks tại đây: Jeffrey Richter trên Windows Memory Model và Richters book CLR qua C # chương 20 có một cách xử lý tuyệt vời:


6
Quy tắc về việc không đặt thành null không phải là "cứng và nhanh" ... nếu đối tượng được đặt vào heap đối tượng lớn (kích thước> 85K), nó sẽ giúp ích cho GC nếu bạn đặt đối tượng thành null khi bạn hoàn thành sử dụng nó.
Scott Dorman

Tôi đồng ý ở một mức độ hạn chế, nhưng trừ khi bạn bắt đầu gặp áp lực bộ nhớ thì tôi thấy không cần phải 'tối ưu hóa sớm' bằng cách đặt các đối tượng thành null sau khi sử dụng.
Kev

21
Toàn bộ hoạt động kinh doanh "không tối ưu hóa sớm" này nghe có vẻ giống như "Thích chậm và đừng lo lắng vì CPU đang ngày càng nhanh hơn và các ứng dụng CRUD không cần tốc độ." Nó chỉ có thể là tôi mặc dù. :)
BobbyShaftoe

19
Điều thực sự có nghĩa là "Trình thu gom rác tốt hơn trong việc quản lý bộ nhớ so với bạn." Đó có thể chỉ là tôi mặc dù. :)
BobRodes

2
@BulkShaftoe: Có lẽ thật sai lầm khi nói "tối ưu hóa sớm là xấu, luôn luôn" vì nó là để nhảy sang thái cực ngược lại của "âm thanh giống như 'thích chậm hơn". Không có lập trình viên hợp lý sẽ nói một trong hai. Đó là về sắc thái và thông minh về những gì bạn tối ưu hóa. Cá nhân tôi lo lắng về sự rõ ràng của mã và hiệu năng KIỂM TRA THỰC SỰ vì cá nhân tôi đã thấy rất nhiều người (bao gồm cả bản thân tôi khi tôi còn trẻ) dành quá nhiều thời gian để tạo ra thuật toán "hoàn hảo", chỉ để nó tiết kiệm 0,1ms trong 100.000 lần lặp tất cả trong khi khả năng đọc đã hoàn toàn bị bắn.
Brent Rittenhouse

36

Một lý do khác để tránh thiết lập các đối tượng thành null khi bạn hoàn thành chúng là nó thực sự có thể giữ chúng tồn tại lâu hơn.

ví dụ

void foo()
{
    var someType = new SomeType();
    someType.DoSomething();
    // someType is now eligible for garbage collection         

    // ... rest of method not using 'someType' ...
}

sẽ cho phép đối tượng được gọi bởi someType trở thành GC'd sau lệnh gọi "DoS Something" nhưng

void foo()
{
    var someType = new SomeType();
    someType.DoSomething();
    // someType is NOT eligible for garbage collection yet
    // because that variable is used at the end of the method         

    // ... rest of method not using 'someType' ...
    someType = null;
}

đôi khi có thể giữ cho đối tượng sống cho đến khi kết thúc phương thức. Các JIT thường sẽ tối ưu hóa đi sự phân công để null , vì vậy cả hai bit cuối mã lên là giống nhau.


Đó là một điểm thú vị. Tôi luôn nghĩ rằng các đối tượng không đi ra khỏi phạm vi cho đến khi phương thức mà chúng nằm trong phạm vi hoàn tất. Tất nhiên trừ khi đối tượng nằm trong phạm vi sử dụng hoặc được đặt rõ ràng là Không có gì hoặc không có giá trị.
Đạo sư Josh

1
Cách ưa thích để đảm bảo rằng họ vẫn còn sống là sử dụng GC.KeepAlive(someType); Xem ericlippert.com/2013/06/10/conloyment-destrraction
NotMe

14

Không có đối tượng null. Bạn có thể xem http://codebetter.com/bloss/karlseguin/archive/2008/04/27/foundations-of-programming-pt-7-back-to-basics-memory.aspx để biết thêm thông tin, nhưng thiết lập mọi thứ để null sẽ không làm gì cả, ngoại trừ làm bẩn mã của bạn.


1
Giải thích hay và chi tiết về bộ nhớ trong liên kết được chia sẻ
2323308

Liên kết bị hỏng. Không có nội dung được liên kết, câu trả lời này khá là usele và nên bị xóa.
Imre Pühvel

7

Cũng thế:

using(SomeObject object = new SomeObject()) 
{
  // do stuff with the object
}
// the object will be disposed of

7

Nói chung, không cần phải bỏ đồ vật sau khi sử dụng, nhưng trong một số trường hợp tôi thấy đó là một cách thực hành tốt.

Nếu một đối tượng thực hiện IDis Dùng một lần và được lưu trữ trong một trường, tôi nghĩ sẽ tốt hơn nếu không sử dụng nó, chỉ để tránh sử dụng đối tượng bị loại bỏ. Các lỗi thuộc loại sau đây có thể gây đau đớn:

this.myField.Dispose();
// ... at some later time
this.myField.DoSomething();

Thật tốt khi hủy trường sau khi xử lý nó và nhận NullPtrEx ngay tại dòng nơi trường được sử dụng lại. Nếu không, bạn có thể gặp phải một số lỗi khó hiểu ở dòng này (tùy thuộc vào chính xác những gì DoS Something làm).


8
Chà, một đối tượng bị loại bỏ sẽ ném ObjectDisposedException nếu nó đã được xử lý. Điều này, theo như tôi biết, yêu cầu mã soạn sẵn ở khắp mọi nơi, nhưng sau đó, một lần nữa, Vứt bỏ là một mô hình suy nghĩ tồi tệ.
nicodemus13

3
Ctrl + F cho .Dispose(). Nếu bạn tìm thấy nó, bạn không sử dụng IDis Dùng chính xác. Việc sử dụng duy nhất cho một đối tượng dùng một lần nên nằm trong giới hạn của khối sử dụng. Và sau khi sử dụng khối, bạn thậm chí không có quyền truy cập myFieldnữa. Và trong khối sử dụng, nullkhông cần cài đặt, khối sử dụng sẽ loại bỏ đối tượng cho bạn.
Suamere

7

Rất có thể là mã của bạn không được cấu trúc chặt chẽ nếu bạn cảm thấy cần phải nullbiến.

Có một số cách để giới hạn phạm vi của một biến:

Như đã đề cập bởi Steve Tranby

using(SomeObject object = new SomeObject()) 
{
  // do stuff with the object
}
// the object will be disposed of

Tương tự, bạn chỉ cần sử dụng dấu ngoặc nhọn:

{
    // Declare the variable and use it
    SomeObject object = new SomeObject()
}
// The variable is no longer available

Tôi thấy rằng việc sử dụng dấu ngoặc nhọn mà không có bất kỳ "tiêu đề" nào để thực sự xóa mã và giúp làm cho nó dễ hiểu hơn.


Tôi đã thử sử dụng phạm vi tùy chỉnh cục bộ một lần (chủ yếu là một smarta $$). Công ty bùng nổ.
Suamere

Một lưu ý khác: Điều này là do trình biên dịch c # sẽ tìm thấy các biến trong phạm vi cục bộ triển khai IDis Dùng một lần và sẽ gọi .Dispose (MOST Of the time) khi phạm vi của chúng kết thúc. Tuy nhiên ... Kết nối SQL là một thời điểm lớn khi .Dispose () không bao giờ được tối ưu hóa. Có một số loại đòi hỏi sự chú ý rõ ràng, vì vậy cá nhân tôi luôn làm mọi thứ rõ ràng chỉ để tôi không bị cắn.
Suamere

5

Lần duy nhất bạn nên đặt biến thành null là khi biến không đi ra khỏi phạm vi và bạn không còn cần dữ liệu liên quan đến nó. Nếu không thì không cần.


2
Điều đó đúng, nhưng điều đó cũng có nghĩa là bạn có thể nên cấu trúc lại mã của mình. Tôi không nghĩ rằng tôi đã bao giờ cần phải khai báo một biến ngoài phạm vi dự định của nó.
Karl Seguin

2
Nếu "biến" được hiểu là bao gồm các trường đối tượng, thì câu trả lời này có rất nhiều ý nghĩa. Trong trường hợp "biến" chỉ có nghĩa là "biến cục bộ" (của một phương thức), thì có lẽ chúng ta đang nói về các trường hợp thích hợp ở đây (ví dụ: một phương thức chạy trong khoảng thời gian dài hơn nhiều so với thông thường).
stakx - không còn đóng góp

5

Nói chung không cần phải đặt thành null. Nhưng giả sử bạn có chức năng Reset trong lớp.

Sau đó, bạn có thể làm, bởi vì bạn không muốn gọi xử lý hai lần, vì một số Loại bỏ có thể không được thực hiện chính xác và ném ngoại lệ System.ObjectDisposed.

private void Reset()
{
    if(_dataset != null)
    {
       _dataset.Dispose();
       _dataset = null;
    }
    //..More such member variables like oracle connection etc. _oraConnection
 }

Tốt nhất chỉ nên theo dõi điều này với một lá cờ riêng biệt có thể.
Thulani Chivandikwa

3

loại "không cần thiết phải đặt đối tượng thành null sau khi sử dụng" là không hoàn toàn chính xác. Có những lúc bạn cần NULL biến sau khi loại bỏ nó.

Có, bạn LUÔN LUÔN gọi .Dispose()hoặc .Close()trên bất cứ thứ gì có nó khi bạn hoàn thành. Có thể là tập tin xử lý, kết nối cơ sở dữ liệu hoặc các đối tượng dùng một lần.

Khác biệt với đó là mô hình rất thực tế của LazyLoad.

Nói rằng tôi có và khởi tạo ObjAcủa class A. Class Acó một tài sản công cộng gọi PropBcủaclass B .

Trong nội bộ, PropBsử dụng biến riêng _Bvà mặc định thành null. Khi PropB.Get()được sử dụng, kiểm tra để xem nếu _PropBlà null và nếu có, mở nguồn lực cần thiết để khởi tạo một Bthành _PropB. Sau đó nó trở lại _PropB.

Theo kinh nghiệm của tôi, đây là một mẹo thực sự hữu ích.

Trường hợp cần null là nếu bạn đặt lại hoặc thay đổi A theo một cách nào đó mà nội dung của nó _PropBlà con của các giá trị trước đó A, bạn sẽ cần loại bỏ VÀ null _PropBđể LazyLoad có thể đặt lại để lấy đúng giá trị NẾU mã đòi hỏi nó

Nếu bạn chỉ làm _PropB.Dispose()và ngay sau khi mong đợi kiểm tra null cho LazyLoad thành công, nó sẽ không có giá trị và bạn sẽ xem xét dữ liệu cũ. Trong thực tế, bạn phải null nó sauDispose() chỉ để chắc chắn.

Tôi chắc chắn muốn nó là khác, nhưng tôi đã có mã ngay bây giờ hiện tính năng này sau một Dispose()trên _PropBvà bên ngoài của hàm gọi đó đã làm các Dispose (và do đó gần như ra khỏi phạm vi), các prop tin vẫn không phải là null, và dữ liệu cũ vẫn còn đó.

Cuối cùng, tài sản bị loại bỏ sẽ bị loại bỏ, nhưng đó không phải là yếu tố quyết định theo quan điểm của tôi.

Lý do cốt lõi, như dbkk ám chỉ là thùng chứa cha ( ObjAvới PropB) đang giữ thể hiện _PropBtrong phạm vi, mặc dù Dispose().


Ví dụ tốt cho thấy cách thiết lập thành null theo cách thủ công có nghĩa là một lỗi nghiêm trọng hơn đối với người gọi đó là một điều tốt.
cuộn

1

Có một số trường hợp có ý nghĩa đối với tài liệu tham khảo null. Chẳng hạn, khi bạn viết một bộ sưu tập - như hàng đợi ưu tiên - và theo hợp đồng của bạn, bạn không nên giữ các đối tượng đó cho khách hàng sau khi khách hàng đã xóa chúng khỏi hàng đợi.

Nhưng loại điều này chỉ quan trọng trong các bộ sưu tập sống lâu. Nếu hàng đợi sẽ không tồn tại đến cuối chức năng mà nó được tạo ra, thì nó ít quan trọng hơn nhiều.

Nhìn chung, bạn thực sự không nên bận tâm. Hãy để trình biên dịch và GC thực hiện công việc của chúng để bạn có thể thực hiện công việc của mình.


1

Hãy xem bài viết này là tốt: http://www.codeproject.com/KB/cs/idis Dùng.aspx

Đối với hầu hết các phần, việc đặt một đối tượng thành null không có hiệu lực. Lần duy nhất bạn nên chắc chắn làm như vậy là nếu bạn đang làm việc với một "vật thể lớn", có kích thước lớn hơn 84K (chẳng hạn như bitmap).


1

Stephen Cleary giải thích rất rõ trong bài đăng này: Tôi có nên đặt Biến thành Null để hỗ trợ Bộ sưu tập rác không?

Nói:

Câu trả lời ngắn, dành cho người thiếu kiên nhẫn Có, nếu biến là trường tĩnh hoặc nếu bạn đang viết một phương thức vô số (sử dụng trả về lợi tức) hoặc phương pháp không đồng bộ (sử dụng async và chờ đợi). Mặt khác, không.

Điều này có nghĩa là trong các phương thức thông thường (không thể đếm được và không đồng bộ), bạn không đặt các biến cục bộ, tham số phương thức hoặc các trường đối tượng thành null.

(Ngay cả khi bạn đang triển khai IDis Dùng. Giả sử, bạn vẫn không nên đặt biến thành null).

Điều quan trọng mà chúng ta nên xem xét là Trường tĩnh .

Các trường tĩnh luôn là các đối tượng gốc , vì vậy chúng luôn được coi là còn sống người thu gom rác . Nếu một trường tĩnh tham chiếu đến một đối tượng không còn cần thiết nữa, thì nó phải được đặt thành null để trình thu gom rác sẽ coi nó là đủ điều kiện để thu thập.

Đặt các trường tĩnh thành null là vô nghĩa nếu toàn bộ quá trình đang tắt. Toàn bộ đống sắp sửa được thu gom tại thời điểm đó, bao gồm tất cả các đối tượng gốc.

Phần kết luận:

Các trường tĩnh ; đó là về nó Bất cứ điều gì khác là một sự lãng phí thời gian .


0

Tôi tin rằng theo thiết kế của những người triển khai GC, bạn không thể tăng tốc độ vô hiệu hóa GC. Tôi chắc rằng họ muốn bạn lo lắng mình với làm thế nào / khi GC chạy - điều trị nó như phổ biến này Being bảo vệ và trông chừng và ra cho bạn ... (cúi đầu xuống, nâng bàn tay lên trời) .. .

Cá nhân, tôi thường đặt các biến thành null một cách rõ ràng khi tôi hoàn thành chúng như một dạng tài liệu tự. Tôi không khai báo, sử dụng, sau đó đặt thành null sau - Tôi null ngay sau khi chúng không còn cần thiết nữa. Tôi đang nói một cách dứt khoát, "Tôi chính thức xong việc với bạn ... biến mất ..."

Là vô hiệu hóa cần thiết trong một ngôn ngữ GC? Không. Nó có hữu ích cho GC không? Có thể có, có thể không, không biết chắc chắn, bằng thiết kế tôi thực sự không thể kiểm soát nó, và bất kể câu trả lời ngày nay với phiên bản này hay điều đó, việc triển khai GC trong tương lai có thể thay đổi câu trả lời ngoài tầm kiểm soát của tôi. Ngoài ra, nếu / khi nulling được tối ưu hóa, nó sẽ ít hơn một nhận xét ưa thích nếu bạn muốn.

Tôi nghĩ rằng nếu nó làm cho ý định của tôi rõ ràng hơn với kẻ ngốc nghèo tiếp theo theo bước chân của tôi, và nếu đôi khi nó "có thể" có khả năng giúp đỡ GC, thì nó đáng để tôi làm. Chủ yếu là nó làm cho tôi cảm thấy gọn gàng và rõ ràng, và Mongo thích cảm thấy gọn gàng và rõ ràng. :)

Tôi nhìn nó như thế này: Các ngôn ngữ lập trình tồn tại để cho phép mọi người cho người khác biết ý định và trình biên dịch yêu cầu công việc phải làm - trình biên dịch chuyển đổi yêu cầu đó sang ngôn ngữ khác (đôi khi là một số) cho CPU - (các) CPU có thể cung cấp cho bạn một ngôn ngữ mà bạn đã sử dụng, cài đặt tab, nhận xét, dấu cách điệu, tên biến, v.v. - CPU là tất cả về luồng bit cho nó biết đăng ký và opcodes và vị trí bộ nhớ để xoay vòng. Nhiều thứ được viết bằng mã không chuyển đổi thành thứ mà CPU tiêu thụ theo trình tự chúng tôi đã chỉ định. C, C ++, C #, Lisp, Babel, lắp ráp của chúng tôi hoặc bất cứ điều gì là lý thuyết hơn là thực tế, được viết như một tuyên bố của công việc. Những gì bạn thấy không phải là những gì bạn nhận được, vâng, ngay cả trong ngôn ngữ trình biên dịch.

Tôi hiểu suy nghĩ của "những thứ không cần thiết" (như dòng trống) "không có gì ngoài tiếng ồn và làm lộn xộn mã." Đó là tôi sớm hơn trong sự nghiệp của tôi; Tôi hoàn toàn hiểu điều đó. Tại thời điểm này, tôi nghiêng về phía mà làm cho mã rõ ràng hơn. Không giống như tôi thêm 50 dòng "tiếng ồn" vào các chương trình của mình - đó là một vài dòng ở đây hoặc ở đó.

Có ngoại lệ cho bất kỳ quy tắc. Trong các trường hợp có bộ nhớ biến động, bộ nhớ tĩnh, điều kiện cuộc đua, singletons, sử dụng dữ liệu "cũ" và tất cả các loại mục nát đó, điều đó khác nhau: bạn CẦN quản lý bộ nhớ của riêng mình, khóa và vô hiệu hóa như apropos vì bộ nhớ không phải là một phần của Vũ trụ của GC - hy vọng mọi người hiểu điều đó. Thời gian còn lại với các ngôn ngữ của GC là vấn đề về phong cách hơn là sự cần thiết hoặc tăng hiệu suất được đảm bảo.

Vào cuối ngày, hãy chắc chắn rằng bạn hiểu những gì đủ điều kiện cho GC và những gì không; khóa, vứt bỏ và vô hiệu hóa một cách thích hợp; sáp trên, sáp tắt; hít vào thở ra; và cho mọi thứ khác tôi nói: Nếu cảm thấy tốt, hãy làm nó. Số dặm của bạn có thể thay đổi ... vì nó nên ...


0

Tôi nghĩ rằng thiết lập một cái gì đó trở lại null là lộn xộn. Hãy tưởng tượng một kịch bản trong đó mục được đặt thành bây giờ được hiển thị thông qua thuộc tính. Bây giờ bằng cách nào đó, một số đoạn mã vô tình sử dụng thuộc tính này sau khi mục bị loại bỏ, bạn sẽ nhận được một ngoại lệ tham chiếu null, yêu cầu một số điều tra để tìm ra chính xác những gì đang diễn ra.

Tôi tin rằng các khung xử lý khung sẽ cho phép ném ObjectDisposedException có ý nghĩa hơn. Không đặt những thứ này trở lại null sẽ tốt hơn vì lý do đó.


-1

Một số đối tượng cho rằng .dispose()phương thức buộc tài nguyên bị xóa khỏi bộ nhớ.


11
Không, nó không có; Vứt bỏ () không thu thập đối tượng - nó được sử dụng để thực hiện dọn dẹp xác định, thường giải phóng các tài nguyên không được quản lý.
Marc Gravell

1
Hãy nhớ rằng tính xác định chỉ áp dụng cho các tài nguyên được quản lý, chứ không phải các tài nguyên không được quản lý (tức là bộ nhớ)
nicodemus13
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.