.NETでの使用後にオブジェクトをNull / Nothingに設定する


187

それらを使い終わったら、すべてのオブジェクトをnullNothingVB.NETで)に設定する必要がありますか?

.NETでは、IDisposable一部のリソースを解放するためにインターフェースを実装するオブジェクトのインスタンスを破棄することが不可欠であると理解しisDisposedています。メモリまたは少なくとも部分的に?

また、オブジェクトがスコープから外れると、ガベージコレクターの次のパスの準備ができるようにマークされます(ただし、これには時間がかかる場合があります)。

したがって、これを念頭に置いnullて、システムがメモリを解放する速度を上げるように設定すると、それがスコープ内になく、悪い副作用があるかどうかを確認する必要がなくなります。

MSDNの記事では例ではこれを行っていません。現在、害を確認できないため、これを行っています。しかし、私は意見の混合に遭遇したので、コメントは役に立ちます。


4
素晴らしい質問を1つ。コンパイラーが割り当てを完全に最適化する状況を誰かが知っていますか?つまり、さまざまな状況下でMSILを確認し、オブジェクトをnullに設定した(またはその欠如)ILを指摘した人がいます。
Tim Medora

回答:


73

Karlは完全に正しいです。使用後にオブジェクトをnullに設定する必要はありません。オブジェクトがを実装している場合は、そのオブジェクトの処理が完了したときに(.. 、またはブロックにラップされて)IDisposable呼び出さIDisposable.Dispose()れることを確認してください。ただし、を呼び出すことを忘れたとしても、オブジェクトのfinalizerメソッドが呼び出す必要があります。tryfinallyusing()Dispose()Dispose()

これは良い治療法だと思いました:

IDisposableを掘り下げる

この

IDisposableを理解する

GCと自己管理型のチューニングは不透明であるため、GCとその管理戦略を推測し直しても意味がありません。Dot Net RocksのJeffrey Richterとの内部の仕組みについては、ここで良い議論がありました。WindowsMemory ModelのJeffrey RichterC#の第20章を介した RichtersブックCLRは、すばらしい扱いです。


6
nullに設定しないことに関するルールは「ハードで高速」ではありません...オブジェクトがラージオブジェクトヒープ(サイズが> 85K)に置かれた場合、完了時にオブジェクトをnullに設定すると、GCに役立ちますそれを使用して。
スコットドーマン

限られた範囲で同意しますが、メモリのプレッシャーを経験し始めていない限り、使用後にオブジェクトをnullに設定して「時期尚早に最適化」する必要はないと思います。
ケブ

21
「時期尚早に最適化しない」というこのビジネス全体は、「CPUが高速になり、CRUDアプリはとにかく速度を必要としないので、低速を優先し、心配しないでください。」のように聞こえます。それは私だけかもしれません。:)
BobbyShaftoe 2008

19
それが実際に意味することは、「ガベージコレクターはあなたよりもメモリの管理に優れている」ということです。それは私だけかもしれません。:)
BobRodes 2012年

2
@BobbyShaftoe:「「優先的に低速」のようなサウンド」の反対の極端にジャンプするため、「時期尚早の最適化は常に悪い」と言うのはおそらく間違っています。合理的なプログラマもそうは言いません。それはニュアンスとあなたの最適化についてスマートであることです。多くの人(私が若い頃の私を含む)が「完璧な」アルゴリズムを作成するのに非常に多くの時間を費やしているので、コードの明快さとパフォーマンスを実際にテストすることを個人的に心配しています。読みやすさが完全に撃たれた間、100,000回の繰り返しで。
ブレントリッテンハウス2017

36

オブジェクトの処理が終わったときにオブジェクトをnullに設定しないようにするもう1つの理由は、オブジェクトを実際に長く保持できるためです。

例えば

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

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

「DoSomething」の呼び出し後にsomeTypeによって参照されるオブジェクトをGCできるようにしますが、

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;
}

メソッドの最後まで、オブジェクトを存続させる場合があります。JITは通常ヌルへの割り当てを離れて最適化されます同じであるアップコード端の両方のビットので、。


それは興味深い点です。オブジェクトは、スコープが設定されているメソッドが完了するまで、オブジェクトがスコープから外れないといつも思っていました。もちろん、オブジェクトのスコープがUsingブロック内にないか、明示的にNothingまたはnullに設定されている場合を除きます。
グルジョシュ2014年

1
彼らが生き続けることを確実にするための好ましい方法は、ericlippert.comGC.KeepAlive(someType);
2013/06/

14

オブジェクトをnullにしないでください。詳細については、http://codebetter.com/blogs/karlseguin/archive/2008/04/27/foundations-of-programming-pt-7-back-to-basics-memory.aspxを確認してください。 nullにしても、コードをダーティにする以外は何もしません。


1
共有リンクのメモリに関する詳細な説明
user2323308 '

リンクが壊れています。リンクされたコンテンツがない場合、この回答はかなり役に立たないため、削除する必要があります。
ImrePühvel19年


7

一般に、使用後にオブジェクトをnullにする必要はありませんが、場合によっては、それが適切な方法であることがわかります。

オブジェクトがIDisposableを実装し、フィールドに格納されている場合は、破棄されたオブジェクトの使用を避けるために、オブジェクトをnullにすることをお勧めします。次の種類のバグは痛みを伴う可能性があります。

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

フィールドを破棄した後、フィールドをnullにして、フィールドが再び使用される行でNullPtrExを取得することをお勧めします。そうしないと、(DoSomethingの機能に応じて)不可解なバグが発生する可能性があります。


8
破棄されたオブジェクトは、既に破棄されている場合はObjectDisposedExceptionをスローする必要があります。これは、私の知る限り、至る所に定型コードが必要ですが、それでも、Disposedはとにかく考え抜かれたパラダイムです。
nicodemus13

3
以下のためのCtrl + F .Dispose()。見つかった場合、IDisposableを正しく使用していません。使い捨てオブジェクトの唯一の用途は、usingブロックの範囲内である必要があります。そして、usingブロックの後は、myFieldもうアクセスできません。また、usingブロック内では、toに設定するnull必要はありません。usingブロックはオブジェクトを破棄します。
Suamere

7

null変数の必要性を感じた場合、コードが十分にしっかりと構造化されていない可能性があります。

変数のスコープを制限する方法はいくつかあります。

スティーブ・トランビーが述べたように

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

同様に、中かっこを使用できます。

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

「見出し」なしで中かっこを使用してコードを実際に整理し、コードをよりわかりやすくしていることがわかりました。


一度カスタムローカルスコープを使用してみました(主にsmarta $$です)。会社が爆発した。
Suamere

別のメモ:これは、c#コンパイラがIDisposableを実装するローカルスコープの変数を検出し、スコープが終了すると.Dispose(ほとんどの場合)を呼び出すためです。ただし... SQL接続は、.Dispose()が最適化されていない場合に非常に重要です。明示的な注意を必要とするタイプがいくつかあるので、私は個人的に常に物事を明示的に行い、噛まれないようにします。
Suamere

5

変数をnullに設定する必要があるのは、変数がスコープ外にならず、それに関連付けられたデータが不要になったときだけです。それ以外の場合は必要ありません。


2
それは事実ですが、コードをリファクタリングする必要があるかもしれません。意図したスコープの外で変数を宣言する必要がなかったと思います。
Karl Seguin

2
「変数」がオブジェクトフィールドを含むと理解されている場合、この答えは非常に理にかなっています。「変数」が(メソッドの)「ローカル変数」のみを意味する場合、おそらくここではニッチなケースについて話している(たとえば、通常よりもはるかに長い時間実行されるメソッド)。
stakx-2017年

5

通常、nullに設定する必要はありません。ただし、クラスにリセット機能があるとします。

次に、disposeを2回呼び出したくないので、そうするかもしれません。Disposeの一部が正しく実装されておらず、System.ObjectDisposed例外がスローされる可能性があるためです。

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

おそらく別のフラグでこれを追跡するのが最善です。
Thulani Chivandikwa

3

この種の「使用後にオブジェクトをnullに設定する必要はありません」は完全に正確ではありません。破棄した後、変数をNULLにする必要がある場合があります。

はい、あなたはいつでもあなたが終わったらそれを持っている.Dispose().Close()何かに電話する必要があります。ファイルハンドル、データベース接続、使い捨てオブジェクトなどです。

それとは別に、LazyLoadの非常に実用的なパターンです。

私が持っていると、インスタンス化と言うObjAclass AClass AというパブリックプロパティがPropBありclass Bます。

内部的にPropBは、プライベート変数を使用し、_Bデフォルトはnullです。PropB.Get()使用されている、それはチェックかどうかを確認するために_PropBはnull、それがある場合で、インスタンス化するのに必要なリソース開くBには_PropB。その後、戻ります_PropB

私の経験では、これは本当に便利なトリックです。

nullが必要になるのは、の内容が_PropB以前の値の子であった何らかの方法でAをリセットまたは変更した場合、コードをIFする場合、LazyLoadがリセットして正しい値をフェッチできるようAにDispose AND null outする必要があり_PropBます。それが必要です。

_PropB.Dispose()LazyLoadのnullチェックが成功することを期待して間もなく実行した場合、それはnullにはならず、古いデータが表示されます。実際には、Dispose()念のためにnullにする必要があります。

私は確かにそれはそうだったら、私は、今後のこの挙動を示すコードを持っているDispose()上で_PropB、処分をした関数呼び出しの外(したがって、ほぼアウトスコープの)、民間の小道具はまだnullではありません古いデータはまだ残っています。

最終的に、破棄されたプロパティは無効になりますが、私の観点からはそれは非決定的です。

中心的な理由は、dbkkが示唆しているように、親コンテナ(ObjAwith PropB)がのインスタンスを_PropBスコープ内に保持していることDispose()です。


手動でnullに設定する方法を示す良い例は、呼び出し元にとってより致命的なエラーを意味し、これは良いことです。
ロール

1

null参照が理にかなっている場合があります。たとえば、優先キューなどのコレクションを作成しているときに、契約によって、クライアントがそれらのオブジェクトをキューから削除した後、それらのオブジェクトをクライアントのために存続させるべきではありません。

しかし、この種のことは長寿命のコレクションでのみ重要です。キューが、それが作成された関数の最後まで存続しない場合は、まったく重要ではありません。

概して、あなたは本当に気にするべきではありません。コンパイラーとGCにそれぞれの仕事を任せて、自分でできるようにします。



1

Stephen Clearyはこの投稿で非常によく説明しています:ガベージコレクションを支援するために変数をNullに設定する必要がありますか?

言う:

変数が静的フィールドの場合、または列挙可能なメソッド(yield returnを使用)または非同期メソッド(asyncおよびawaitを使用)を記述している場合、せっかちなはいの場合の短い回答。そうでなければ、いいえ。

これは、通常のメソッド(列挙および非同期ではない)では、ローカル変数、メソッドパラメーター、またはインスタンスフィールドをnullに設定しないことを意味します。

(IDisposable.Disposeを実装している場合でも、変数をnullに設定しないでください)。

考慮すべき重要なことは、静的フィールドです。

静的フィールドは常にルートオブジェクトであるため、ガベージコレクターによって常に「生きている」と見なされます。静的フィールドが不要になったオブジェクトを参照する場合は、それをnullに設定して、ガベージコレクターがオブジェクトをコレクションの対象として扱うようにする必要があります。

プロセス全体がシャットダウンしている場合、静的フィールドをnullに設定しても意味がありません。その時点で、すべてのルートオブジェクトを含むヒープ全体がガベージコレクションされます。

結論:

静的フィールド ; それだけです。それ以外のものは時間の無駄です


0

私は、GCインプリメンターの設計により、無効化でGCを高速化できないと信じています。GCがどのように/いつ実行されるかを気にしないでほしいと私は確信している-ユビキタスな存在のように扱う、あなたのために...(弓は頭を下に、空に拳上げ)を保護し、見守ります。.. 。

個人的には、セルフドキュメンテーションの形式として変数を使い終わったら、変数を明示的にnullに設定することがよくあります。宣言したり、使用したり、後でnullに設定したりはしません-不要になった直後にnullにします。はっきり言って、「私は正式にあなたと一緒に終わりました...行ってしまいます...」

GC化された言語では無効化が必要ですか?いいえ。GCに役立ちますか?たぶんはい、多分いいえ、確かにはわかりません。設計上、実際には制御できません。このバージョンでの今日の回答に関係なく、将来のGC実装は私の制御を超えて回答を変える可能性があります。加えて、nullが最適化されている場合、または最適化されている場合は、ファンシーなコメントに過ぎません。

私の足跡をたどる次の貧しい愚か者に私の意図がより明確になるかどうか、そしてそれが時々GCに役立つかもしれない「可能性」があるなら、それは私にとってそれだけの価値があります。主にそれは私がきちんとしていてクリアであることを感じさせます、そして、モンゴはきちんとしていてクリアであることを好みます。:)

私はそれを次のように見ています:プログラミング言語は、他の人々に意図の意図とコンパイラーに何をすべきかのジョブ要求を与えることを可能にするために存在します-コンパイラーはその要求をCPUの異なる言語(時にはいくつか)に変換します- CPUは、使用した言語、タブ設定、コメント、文体的強調、変数名などを簡単に提供できます。CPUは、ビットストリームがすべてのレジスタとオペコード、メモリの場所をいじるのを指示します。コードで書かれた多くのものが、指定したシーケンスでCPUが消費するものに変換されません。私たちのC、C ++、C#、Lisp、Babel、アセンブラ、または現実ではなく理論であるものはすべて、作業明細書として書かれています。あなたが見るものは、あなたが得るものではありません、はい、アセンブラー言語でさえです。

「不要なもの」(空白行など)の考え方は「ノイズでコードが乱雑になる」に過ぎません。それは私のキャリアの初期の私でした。完全にわかりました。この時点で、コードをより明確にするものに傾いています。プログラムに50行の「ノイズ」を追加しているのではなく、あちこちに数行あります。

ルールには例外があります。揮発性メモリ、静的メモリ、競合状態、シングルトン、「古い」データの使用、およびそのようなすべての腐敗があるシナリオでは、それは異なります。独自のメモリを管理する必要があり、メモリが含まれていないため、適切にロックおよび無効化する必要があります。 GC'd Universe-うまくいけば誰もがそれを理解します。それ以外の時間は、GCの言語では、必要性や保証されたパフォーマンスの向上ではなく、スタイルの問題です。

1日の終わりに、GCに適格なものと適格でないものを必ず理解してください。適切にロック、破棄、無効化します。ワックスオン、ワックスオフ; 息を吸って、息を吐いて; そして私が言う他のすべてのために:それが気分が良いなら、それをしてください。あなたの走行距離は変わるかもしれません...


0

何かをnullに戻すのは厄介だと思います。現在設定されているアイテムがプロパティ経由で公開されるシナリオを想像してみてください。アイテムが破棄された後、コードの一部がこのプロパティを誤って使用するようになったため、何が行われているのかを正確に把握するために調査が必要なnull参照例外が発生します。

フレームワークのディスポーザブルは、より意味のあるObjectDisposedExceptionをスローできると思います。そのため、これらをnullに戻さない方が良いでしょう。


-1

一部のオブジェクトは、.dispose()リソースをメモリから削除するメソッドを想定しています。


11
いいえ、ありません。Dispose()はオブジェクトを収集しません。これは、確定的クリーンアップを実行するために使用され、通常はアンマネージリソースを解放します。
Marc Gravell

1
決定論だけで管理対象リソースではなく、管理されていないもの(すなわちメモリ)に適用されることを念頭に
nicodemus13
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.