2009-05-29 5 views
9

次のようなコンストラクタから例外をスローするとメモリリークはありますか?.NETのコンストラクタから例外をスローする

class Victim 
{ 
    public string var1 = "asldslkjdlsakjdlksajdlksadlksajdlj"; 

    public Victim() 
    { 
     //throw new Exception("oops!"); 
    } 
} 

ガベージコレクタは、障害のあるオブジェクトを収集しますか?

+0

かろうじて関連が、便利なヒント:コントロールのコンストラクタでスローされた例外に注意してください。それはコントロール/フォームのデザイナーを壊すことができます。私はInitialise()メソッドを持ってそれを外部に呼び出すことでラウンドしました(しかし、私はそれを好きではありません)。 –

答えて

23

一般に、メモリをリークしないという観点からは安全です。ただし、コンストラクタから例外をスローすると、その型のアンマネージリソースを割り当てると危険です。次の例に

public class Foo : IDisposable { 
    private IntPtr m_ptr; 
    public Foo() { 
    m_ptr = Marshal.AllocHGlobal(42); 
    throw new Exception(); 
    } 
    // Most of Idisposable implementation ommitted for brevity 
    public void Dispose() { 
    Marshal.FreeHGlobal(m_ptr); 
    } 
} 

してください。このクラスはメモリに、あなたが使用してブロックを使用している場合でも、作成しようとするたびにリークします。たとえば、これはメモリをリークします。

using (var f = new Foo()) { 
    // Won't execute and Foo.Dispose is not called 
} 
+0

usingステートメントの本体が実行されないだけでなく、.Dispose()を呼び出すインスタンスが作成されていないため、Fooの.Dispose()メソッドが呼び出されないことを明確にする必要があります。 – jrista

+0

@jrista更新 – JaredPar

+0

私はここでパーティーに真剣に遅れていますが、コンストラクタで試しても最終的にこれを問題なく修正できませんでしたか? – Gusdor

2

はい、ガベージコレクタは、オブジェクトにすでに割り当てられている管理対象リソースを再利用します。管理されていないリソースを初期化した場合は、通常の方法で自分自身をクリーンアップする必要があります。

1

例外が発生する前に取得した他のリソースによって異なります。私はコンストラクタの例外をスローすることは素晴らしいとは思いませんが、ファイナライザに投げたり破棄したりするのはずっと悪いことです。

+2

"ファイナライザでそれらを投げたり破棄したりするのははるかに悪いです"。 Disposeをスローすることもできます。たとえば、Streamをフラッシュできない場合(たとえば、ネットワークエラーのために)、FileStreamがスローします。できるならば投げないようにしてください。そうしなければならない場合はそうしてください。 FileStreamの場合、例外を飲み込むことは、呼び出し側にファイルが正常に書き込まれたという印象を与えるので、スローするよりもはるかに危険です。 – Joe

4

ちょうど昨日similar questionを手伝ったので面白かったです。

派生型の一部が初期化されますが、他の部分は初期化されないため、派生型がある場合は大きな問題です。メモリの観点からは、ガベージコレクタは何がどこにあるかを知っているので、それは本当に重要ではありません。しかし、あなたが管理されていないリソース(IDisposableを実装する)を持っているなら、物事は暗くなる可能性があります。

9

アンマネージリソースを作成していない場合は、コンストラクタから例外をスローすると問題はありません。ただし、コンストラクタでアンマネージリソースを作成する場合は、そのコンストラクタの全身(スローを含む)をtry/catchでラップする必要があります。 JaredParの素晴らしい例を盗むために:

public class Foo : IDisposable { 
    private IntPtr m_ptr; 
    public Foo() { 
    try 
    { 
     m_ptr = Marshal.AllocHGlobal(42); 
     throw new Exception(); 
    } 
    catch 
    { 
     Dispose(); 
     throw; 
    } 
    } 
    // Most of Idisposable implementation ommitted for brevity 
    public void Dispose() { 
    Marshal.FreeHGlobal(m_ptr); 
    } 
} 

以下は今仕事になります。

using (var f = new Foo()) { 
    // Won't execute, but Foo still cleans itself up 
} 
関連する問題