更新:私はこの質問をここにある記事の基礎として使用しました。この問題の詳細については、それを参照してください。良い質問をありがとう!
しかしSchabseの答えもちろん正しいと尋ねられた質問に答える、あなたが要求していない、あなたの質問に重要なバリエーションがあります:
アンマネージリソースがコンストラクターによって割り当てられた後、ただしctorが戻って参照で埋める前にfont4 = new Font()
スローするとどうなりますか?font4
それをもう少し明確にしましょう。次のように仮定します。
public sealed class Foo : IDisposable
{
private int handle = 0;
private bool disposed = false;
public Foo()
{
Blah1();
int x = AllocateResource();
Blah2();
this.handle = x;
Blah3();
}
~Foo()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
if (this.handle != 0)
DeallocateResource(this.handle);
this.handle = 0;
this.disposed = true;
}
}
}
今私たちは持っています
using(Foo foo = new Foo())
Whatever(foo);
これは同じです
{
Foo foo = new Foo();
try
{
Whatever(foo);
}
finally
{
IDisposable d = foo as IDisposable;
if (d != null)
d.Dispose();
}
}
OK。Whatever
投げるとしましょう。次に、finally
ブロックが実行され、リソースが割り当て解除されます。問題ない。
Blah1()
投げるとしましょう。次に、リソースが割り当てられる前にスローが発生します。オブジェクトが割り当てられているが、ctorのは決して戻って、これfoo
に充填されることはありません。私たちは、入力されたことがないtry
私たちが入ることはありませんので、finally
どちらかを。オブジェクト参照は孤立しています。最終的にGCはそれを検出し、ファイナライザキューに入れます。 handle
はまだゼロなので、ファイナライザは何もしません。 ファイナライザは、コンストラクタが完了していないファイナライズ中のオブジェクトに対してロバストである必要があることに注意してください。あなたはされている必要この強力なファイナライザを作成するます。これは、ファイナライザの記述を専門家に任せ、自分でやろうとしないもう1つの理由です。
と思います Blah3()
投げる。スローは、リソースが割り当てられた後に発生します。しかし、ここでも、foo
決して入力されることはなく、を入力することもありませんfinally
。オブジェクトはファイナライザスレッドによってクリーンアップされます。今回はハンドルがゼロではなく、ファイナライザがそれをクリーンアップします。繰り返しになりますが、ファイナライザは、コンストラクタが成功しなかったオブジェクトで実行されていますが、ファイナライザはとにかく実行されます。今回は、やらなければならないことがあったからです。
今、Blah2()
投げると仮定します。スローは、リソースが割り当てられた後、前に発生します handle
します。繰り返しになりますが、ファイナライザは実行されますが、今handle
でもまだゼロであり、ハンドルをリークしています!
このリークが発生しないようにするには、非常に巧妙なコードを書く必要があります。さて、あなたのFont
リソースの場合、一体誰が気にしますか?フォントハンドルをリークします。しかし、あなたがあれば絶対に積極的に必要とすることを、すべての、例外のタイミングに関係なくアンマネージリソースをクリーンアップ場合は、非常に難しい問題を抱えています。
CLRはこの問題をロックで解決する必要があります。C#4以降、lock
ステートメントを使用するロックは次のように実装されました。
bool lockEntered = false;
object lockObject = whatever;
try
{
Monitor.Enter(lockObject, ref lockEntered);
lock body here
}
finally
{
if (lockEntered) Monitor.Exit(lockObject);
}
Enter
ように非常に慎重に書かれている例外がスローされているものに関係なくは、lockEntered
trueに設定されている場合にのみ場合ロックは、ロックが実際に取得されたされています。同様の要件がある場合は、実際に書く必要があります。
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
書き込みAllocateResource
巧みのようなMonitor.Enter
ので、内部に何が起こるかに関係なくがあることAllocateResource
、handle
で満たされています割り当てを解除する必要がある場合にのみます。
そうするためのテクニックを説明することは、この回答の範囲を超えています。この要件がある場合は、専門家に相談してください。