0

クラスコンストラクターの最後の例外など、すべての場合にクラスのメンバー変数が確実にクリーンアップされるようにする方法を探していました。

これらはメンバー変数であるため、「try、catch」および「using」パターンは役に立ちません。.NET C ++(C ++ / clr:safe)は、auto_ptrやshared_ptrなどのスマートポインター(msclr :: auto_handleと呼ばれる)のエミュレーションを提供することに気づきました。これは、スレッドやソケットなどの限られたリソースを非常にクリーンな方法で決定論的に破棄できるため、非常に便利です。

私はC++/ clrで生成されたILを分析していて、実際に行われているように見えるのは、カプセル化されたデータを変更するすべての関数でtry/faultsを使用してILをスパムすることだけであることに気付きました。

興味のある人のためにILリストを含めました。(try / faultは私によって追加されたのではなく、C ++ / clrコンパイラによって追加されました)

  MyClass()
  {
        myDisposable.reset(gcnew MyDisposable());
        throw gcnew Exception("Hello World");
        // myDisposable needs to clean up now
        // because it is very large or locks a limited resource.
        // Luckily with RAII.. it does!
  }

...になります...

  .try
  {
  IL_0006:  ldarg.0
  IL_0007:  ldloc.0
  IL_0008:  stfld      class msclr.'auto_handle<MyDisposable>' modreq([mscorlib]System.Runtime.CompilerServices.IsByValue) MyClass::myDisposable
  IL_000d:  ldarg.0
  IL_000e:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0013:  ldarg.0
  IL_0014:  ldfld      class msclr.'auto_handle<MyDisposable>' modreq([mscorlib]System.Runtime.CompilerServices.IsByValue) MyClass::myDisposable
  IL_0019:  newobj     instance void MyDisposable::.ctor()
  IL_001e:  call       instance void msclr.'auto_handle<MyDisposable>'::reset(class MyDisposable)
  IL_0023:  ldstr      "Hello World"
  IL_0028:  newobj     instance void [mscorlib]System.Exception::.ctor(string)
  IL_002d:  throw
  IL_002e:  leave.s    IL_003c
  }  // end .try
  fault
  {
  IL_0030:  ldarg.0
  IL_0031:  ldfld      class msclr.'auto_handle<MyDisposable>' modreq([mscorlib]System.Runtime.CompilerServices.IsByValue) MyClass::myDisposable
  IL_0036:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
  IL_003b:  endfinally
  }  // end handler

私のソフトウェアは非常に複雑になり、これらすべてを自分で処理することは非常に危険でバグが発生しやすいため、C#でこれを達成する同様の方法はありますか?では、この追加のILコードを自動的に追加して、C#でRAIIをエミュレートできるようにする手法や、ビルド後の手順を知っている人はいますか?

編集:(別の例)

  ref class MyClass
  {
  private:
        msclr::auto_handle<MyDisposable> myDisposable;

  public:
        MyClass()
        {
              myDisposable.reset(gcnew MyDisposable());
              throw gcnew Exception("Hello World");
              // myDisposable needs to clean up now because it is very large or locks a limited resource.
        }
  };

myDisposableはメンバー変数です。コンストラクターから「HelloWorld」がスローされると、myDisposableは実際にはすぐに破棄されます。C#で同じ機能を利用できますか?使用はメンバー変数上にあり、すべての関数でのtry / catchは非常に貧弱な解決策であるため、使用できないことを確認しました。

よろしくお願いします、

Karsten

4

2 に答える 2

5

C++/CLI の auto_handle<> テンプレート クラスは、「スタック セマンティクス」と呼ばれる C++/CLI コンパイラに固有の機能を使用します。C++ プログラマーにとって非常に馴染みのある機能であり、RAII の背後にあるコア ランタイム サポートです。簡単に言えば、コンパイラは、デストラクタがスコープ ブロックの最後で呼び出されるようにします。コードが例外をスローした場合でも確実にデストラクタが呼び出されるように、IL で発行された .try/fault ブロックが表示されます。

auto_handle クラスのデストラクタは、ラップするオブジェクトのデストラクタを呼び出します。C++ との類似点はここまでです。C++/CLI クラスのデストラクタは IDisposable.Dispose() メソッドです。「実際の」デストラクタは、クラスのファイナライザであり、!classname構文で示されます。

C# との類似点は、 usingステートメントとまったく同じです。Dispose() メソッドが確実に呼び出されるようにし、try/finally を使用して、例外が発生した場合でもそれが確実に行われるようにします。デザイナーは、C++/CLI にusingキーワードを追加する代わりに、C++ プログラマーにとってより使い慣れた構文を選択することを選択しました。IDisposable の使用法にも見られるように、Dispose() を呼び出すことはできませんが、delete演算子を使用して呼び出す必要があります。

したがって、C++/CLI で auto_handle<> が好きなら、C# での使用を好むのとまったく同じ理由があります。

C++ の RAII とマネージド コードの auto_handle/using の大きな違いに注意してください。C++ でメモリを解放するために RAII が必要になることがよくありますが、これはマネージ言語ではまったく不要です。あなたもできません。それはガベージコレクターの仕事です。IDisposable を継承するオブジェクトを作成する場合にのみ、 using を使用する必要があります。確かに、.NET のすべてのクラスがそうではありません。また、必須ではなくオプションです。クラスのファイナライザーは、Dispose() で早期に行われなかった場合に、アンマネージ リソースが解放されることを常に保証します。

例外的なケースでは、ざらざらした例外処理や、オブジェクトの存続期間を追跡するのが面倒なため、Dispose() 呼び出しをスキップしても問題ありません。.NET フレームワークにおけるこのようなクラスの標準的な例は、Thread クラスです。5 つの使い捨てネイティブ リソースがありますが、IDisposable を実装していません。確実に呼び出されるようにコードを書くと、スレッドを使用するポイントが無効になる傾向があるためです。

于 2012-08-15T17:05:25.877 に答える
1

using でメンバー変数を使用できます。

class junk
{
    private IDisposable somevar;
    void SomeFunc()
    {
        using (somevar = SomeOtherFunc())
        {
           YesAnotherFunc();
        }
    }
}
于 2012-08-15T17:04:27.343 に答える