13

using」コンストラクトは、開始部分と分離された終了部分の両方を必要とする状況で非常に便利に見えます。

説明する簡単な例:

using (new Tag("body")) {
    Trace.WriteLine("hello!");
}
// ...
class Tag : IDisposable {
    String name;
    public Tag(String name) {
        this.name = name;
        Trace.WriteLine("<" + this.name + ">");
        Trace.Indent();
    }
    public void Dispose() {
        Trace.Unindent();
        Trace.WriteLine("</" + this.name + ">")
    }
}

最初の部分はコンストラクターとして定義され、最後の部分はDisposeメソッドです。

ただし、魅力的であるにもかかわらず、この構成には、Disposeメソッドがfinallyブロック内から呼び出されるという事実に起因する重大な警告があります。したがって、2つの問題があります。

  1. キャッチされるはずだった元の例外をオーバーライドするため、finallyブロックから例外をスローすることは避けてください。

  2. Disposeメソッドの内部で、「beginning」と「end」の間に例外が以前にスローされたかどうかを知る方法はありません。したがって、「end」部分をそれに応じて処理する方法はありません。

これらの2つのことにより、この構成を使用することは実用的ではありません。これは非常に悲しい事実です。さて、私の質問は次のとおりです。

  1. 問題についての私の理解は正しいですか?これは実際に「使用」がどのように機能するのですか?

  2. もしそうなら、これらの問題を克服し、本来の目的以外の「使用」構造を実用化する方法はありますか(リソースの解放とクリーンアップ)

  3. このように「使用する」ための実用的な方法がない場合。代替アプローチは何ですか(最初と最後の部分でいくつかのコードにコンテキストを適用するため)?

4

5 に答える 5

5

ルール#1は、の有無にかかわらず適用されるusingため、ルール#2が実際の決定要因です。例外がスローされた状況と通常のプログラム完了を区別する必要がある場合は、 try/を選択してください。catch

たとえば、永続層がデータベース接続を使用する過程で問題を発見する可能性がある場合、例外があったにもかかわらず、接続を閉じる必要があります。この場合、using構成は完璧な選択です。

場合によってはusing、通常の完了と例外的な完了を検出するように特別に設定できます。アンビエントトランザクションは完璧な例を提供します:

using(TransactionScope scope = new TransactionScope()) {
    // Do something that may throw an exception
    scope.Complete();
}

scopeDispose呼び出される前Completeにが呼び出された場合TransactionScope、例外がスローされたことを認識し、トランザクションを中止します。

于 2012-06-28T15:02:25.117 に答える
2

これが本来の意図であったかどうかはわかりませんIDisposableが、Microsoftは確かにあなたが説明する方法でそれを使用しています(開始部分と終了部分を分離しています)。その良い例は、mvcインフラストラクチャによって提供されるMVCFormクラスです。フォームの終了タグを実装IDisposableして書き込みますが、その実装がantリソースを解放しているのはわかりません(フォームを破棄した後も、HTMLを出力するためにそこで使用されたライターは存続しているようです)。 ブロックと、それが例外を「飲み込む」方法
について多くのことが書かれています( wcfクライアントは良いサンプルです。SOでそのような議論を見つけることもできます)。個人的には、ブロックを使用するのが便利である限り、いつ使用すべきか、いつ使用すべきでないかが100%明確ではないこと も何度も感じています。usingusing

もちろん、実際には、クラスにフラグを追加し、ブロック内でフラグを上げることで、エラーなしで到達したかどうかをdisposeメソッド内で判断できますusingが、これは、クラスを使用する人がいる場合にのみ機能します。そのフラグを認識します

于 2012-06-28T15:26:23.067 に答える
2

usingステートメントとインターフェースの目的はIDisposable、ユーザーが管理されていないリソースを破棄することです。これらのリソースは通常、高価で貴重なものであるため、何があっても廃棄する必要があります(そのため、にありますfinally)。ブロック内のコードをfinally中止することもできず、アプリドメイン全体のシャットダウンがハングする可能性があります。

さて、あなたが説明している目的のために悪用することは非常に魅力的usingです、そして私は過去にそれをしました。ほとんどの場合、そこには危険はありません。ただし、予期しない例外が発生した場合、処理の状態全体が危険にさらされるため、必ずしも終了操作を実行する必要はありません。したがって、一般的に、これを行わないでください。

別の方法は、次のようなラムダを使用することです。

public interface IScopable { 
  void EndScope();
}

public class Tag : IScopable {
  private string name;
  public Tag(string name) {
    this.name = name;
    Trace.WriteLine("<" + this.name + ">");
    Trace.Indent();
  }
  public void EndScope() {
    Trace.Unindent();
    Trace.WriteLine("</" + this.name + ">");
  }
}

public static class Scoping {
  public static void Scope<T>(this T scopable, Action<T> action) 
    where T : IScopable {
    action(scopable);
    scopable.EndScope();
  }
}

次のように使用します。

new Tag("body").Scope(_ => 
  Trace.WriteLine("hello!")
);

例外が発生したかどうかに基づいて特定のアクションを実行する他の実装を作成することもできます。

Nemerleでは、これをサポートするために言語を新しい構文で拡張できます。

于 2012-06-29T19:08:39.927 に答える
1

try / finalブロックの設計に関する問題を観察することは正しいです。これは、次の問題です。ブロック内のコードが、ブロックに続くステートメントでコード実行を続行するかどうかを知るusingためのクリーンな方法はありません。ブロックが実行されるとすぐに効果的に引き継がれる保留中の例外があります。finallyfinallyfinally

finallyブロックにExceptionパラメーターを含めることができるvb.netとC#の言語機能を本当に見たいです(例:

  試す
  {{
  }
  最後に(例外例)
  {{
    ..。
  }

渡された例外はnulltryブロックが正常に終了した場合、または終了しなかった場合に例外を保持する場合です。IDisposableExこれに加えて、を継承し、ユーザーコードがブロックから渡されることを期待Disposeするメソッドを含むインターフェイスを確認したいと思います。中に発生した例外は、渡された例外をラップする可能性があります(渡された例外と、で例外が発生したという事実の両方が関連するため)。Dispose(Exception ex)exfinallyDisposeDispose

それができない場合は、現在のコンテキストで保留中の例外があったかどうかを示すメソッドを.netに提供させると役立つ場合があります。残念ながら、そのようなメソッドの正確なセマンティクスがさまざまなコーナーケースでどうあるべきかは明確ではありません。対照的に、のセマンティクスはfinally (Exception ex)完全に明確です。ちなみに、を適切に実装するにfinally (Exception ex)は、言語で例外フィルターを使用する必要がありますが、任意のフィルターを作成する機能を公開する必要はありません。

于 2012-06-28T15:41:22.013 に答える
0

私の意見では、あなたはIDisposableインターフェースを誤用しています。一般的な方法は、インターフェイスを使用して管理されていないリソースを解放することです。通常、ガベージコレクターはオブジェクトをクリーンアップしますが、場合によっては、オブジェクトが不要になったときに手動でクリーンアップする必要があります。

ただし、あなたの場合、不要になったオブジェクトをクリーンアップしているわけではありません。いくつかのロジックを強制するためにそれを使用しています。そのために別のデザインを使用する必要があります。

于 2012-06-28T15:01:10.363 に答える