2009-11-29 11 views
14

C#のコーディングパターンを調査しています。このクラスでは、 "使用する"本体が正常に終了したか例外を出したかによって、Dispose()メソッドが異なる処理を行う特別なクラスで "using"句を使用します。 。.NET例外が処理されているかどうかを判断する方法?

私の理解している限り、CLRは "catch"ハンドラによって消費されるまで処理されている現在の例外を追跡します。しかし、コードがアクセスするためにこの情報が何らかの方法で公開されているかどうかは完全には明らかではありません。あなたはそれがあるかどうかを知っていますか?もしそうなら、それにアクセスする方法はありますか?例えば

using (var x = new MyObject()) 
{ 
    x.DoSomething(); 
    x.DoMoreThings(); 
} 

class MyObject : IDisposable 
{ 
    public void Dispose() 
    { 
     if (ExceptionIsBeingHandled) 
      Rollback(); 
     else 
      Commit(); 
    } 
} 

その成功/失敗はx.Complete()への呼び出しによって決まるのではなく、using本体が正常に終了したかどうかに基づいていないことを除いてこれは、ほとんどSystem.Transactions.TransactionScopeのように見えます。

+2

私はあなたがなぜこれをやろうとしているのか尋ねる必要があると思います。 Dispose()パターンは、例外を発生させて制御ロジックを実装するためのものではありません。 –

+1

全体のアイデアに疑問を呈することは、常に公正なポイントです。私はこれが "使用する"という意味ではないことに感謝します。私はそれが悪いコードにつながることができて感謝します。私はまだ答えに興味があります:) –

+1

この質問はなぜ下降ですか? – Olli

答えて

11

http://www.codewrecks.com/blog/index.php/2008/07/25/detecting-if-finally-block-is-executing-for-an-manhandled-exception/には、コードが例外処理モードで実行されているかどうかを検出する「ハック」が記載されています。 Marshal.GetExceptionPointersを使用して、例外が「アクティブ」であるかどうかを確認します。

しかし、心に留めておく:

備考

GetExceptionPointersだけ(SEH)を構造化例外処理のコンパイラのサポートのために露出されます。 注:

このメソッドでは、SecurityAction.LinkDemandを使用して、信頼できないコードからの呼び出しを防止します。 SecurityPermissionAttribute.UnmanagedCodeパーミッションを持つ直前の呼び出し元だけが必要です。コードを部分的に信頼できるコードから呼び出すことができる場合は、検証を行わずにユーザー入力をMarshalクラスのメソッドに渡さないでください。 LinkDemandメンバーの使用に関する重要な制限については、「Demand vs. LinkDemand」を参照してください。

+5

これはおそらくあなたの質問に対する唯一の実際の答えですが、追求することは深刻な悪い考えです。 –

+0

ありがとうございました。 @プログラマブルヒーロー:おそらく。そんなことは否定できない。私たちは小さなテストプロジェクトでこれを実行し、このアプローチに大きな問題があるかどうかを確認します。 –

+2

この道を行くなら、最初に次のブログを見てください:http://geekswithblogs.net/akraus1/archive/2008/04/08/121121。aspx – Joe

2

usingステートメントはtry finallyブロックの文法的な砂糖です。あなたはフルで出て、最終的に試みを書いた後、あなたの特別なケースを処理するcatchステートメントを追加することによって、あなたが欲しいものを得ることができます。

try 
{ 
    IDisposable x = new MyThing(); 
} 
catch (Exception exception) // Use a more specific exception if possible. 
{ 
    x.ErrorOccurred = true; // You could even pass a reference to the exception if you wish. 
    throw; 
} 
finally 
{ 
    x.Dispose(); 
} 

インサイドMyThingしたい場合は、たとえば、これを行うことができます。

class MyThing : IDisposable 
{ 
    public bool ErrorOccurred() { get; set; } 

    public void Dispose() 
    { 
     if (ErrorOccurred) { 
      RollBack(); 
     } else { 
      Commit(); 
     } 
    } 
} 

注:私はあなたがこれをやりたい理由も疑問に思っています。それはいくつかのコードのにおいがあります。 Disposeメソッドは、例外を処理するのではなく、アンマネージリソースをクリーンアップするためのものです。おそらく、例外処理コードを処理ブロックではなくcatchブロックに書き込む方が良いでしょう。また、コードを共有する必要がある場合は、両方の場所から呼び出せる便利なヘルパー関数を作成することをお勧めします。

はここで何をしたい行うためのより良い方法です:

using (IDisposable x = new MyThing()) 
{ 
    x.Foo(); 
    x.Bar(); 
    x.CommitChanges(); 
} 

class MyThing : IDisposable 
{ 
    public bool IsCommitted { get; private set; } 

    public void CommitChanges() 
    { 
     // Do stuff needed to commit. 
     IsCommitted = true; 
    } 

    public void Dispose() 
    { 
     if (!IsCommitted) 
      RollBack(); 
    } 
} 
+0

ああ、私はこれを言及すべきでした。要点は、 "catch"と "finally"に入るコードは同一であるが、それも重要ではないことです。ポイントは、このコードを別の場所に移動することです。 –

+0

これは問題ありません。 disposeコードはどちらの場合でも呼び出されますが、catchコードではdisposeが呼び出される前に* extra *コードを実行できます。たとえば、xの内側にブール値を設定することができます。これは、Disposeが呼び出されたときにチェックすることができます。 –

+0

あなたが努力してくれてありがとうございますが、まだ "通常の" try/catch節を置かなければならない場合は、この点には何も言及していません - あなたが正しく観察したように、 'if(ErrorOccurred/else') 'catch' /' finally'の中に入ってください。このアイデアは、繰り返し使用するtry/catch/finallyを 'using'で保存することです。なぜなら、それは定型的なコードを全面的に減らすからです。 –

4

この情報はあなたに利用できません。

DbTransactionクラスで使用されているパターンと似たパターンを使用します。すなわち、IDisposableクラスはDbTransaction.Commit()と類似のメソッドを実装する必要があります。 Disposeメソッドは、Commitが呼び出されたかどうかによって異なるロジックを実行できます(DbTransactionの場合、トランザクションは明示的にコミットされていないとロールバックされます)。私はあなたが(あなたがこのパターンを認識しています表示するには、あなたの質問を編集した参照

using(MyDisposableClass instance = ...) 
{ 
    ... do whatever ... 

    instance.Commit(); 
} // Dispose logic depends on whether or not Commit was called. 

EDIT:あなたのクラスの

ユーザーは、典型的なDbTransactionに似た次のパターンを使用します例はTransactionScopeを使用します)。それにもかかわらず、私はそれが唯一の現実的な解決策だと思う。

2

これは悪い考えではありません。 C#/。NETでは理想的ではないようです。

C++には、例外のためにコードが呼び出されているかどうかをコードが検出できるようにする関数があります。これはRAIIデストラクタで最も重要です。デストラクタが、制御フローが正常であるか例外的であるかによって、コミットまたはアボートすることを選択することは些細なことです。これはかなり自然なアプローチだと思いますが、組み込みのサポートの欠如(そして回避策の道徳的に疑わしい性質、それはむしろ私にとって実装に依存していると感じます)はおそらくもっと従来のアプローチを取るべきことを意味します。

7

質問に答えることはできませんが、実際のコードで「受け入れられた」ハックを使用することはありませんでしたので、まだ「野生の中で」テストされていません。代わりに、私たちはこのような何かのために行ってきました:キーポイントは、それが問題の例「を使用して」とコンパクトとしてだし、任意のハックを使用していないということです

public void DoThings(Action<MyObject> action) 
{ 
    bool success = false; 
    try 
    { 
     action(new MyObject()); 
     Commit(); 
     success = true; 
    } 
    finally 
    { 
     if (!success) 
      Rollback(); 
    } 
} 

DoThings(x => 
{ 
    x.DoSomething(); 
    x.DoMoreThings(); 
}); 

パフォーマンスペナルティ(私たちのケースでは完全に無視できる)とF10はDoThingsに進んで、実際にはx.DoSomething()にまっすぐに進んでほしいと思っています。どちらも非常にマイナーです。

+3

これは、匿名の方法でIntelliSenseのバグが発生するため、VS2010ではあまり魅力的ではありません... https://connect.microsoft.com/VisualStudio/feedback/details/557224/intellisense-completely-fails-for-collection-inside-匿名メソッド?wa = wsignin1.0 –

関連する問題