5
using(SomeClass x = new SomeClass("c:/temp/test.txt"))
{
...
}

using ブロック内では、例外を通常どおりに処理するだけで問題ありません。しかし、 のコンストラクターがSomeClass例外をスローできる場合はどうなるでしょうか?

4

5 に答える 5

5

あなたの使い方をtry catch feに入れます

try
{
   using(SomeClass x = new SomeClass("c:/temp/test.txt"))
   {
       ...
   }
}
catch(Exception ex)
{
   ...
}
于 2010-07-29T09:06:42.260 に答える
4

はい、これはコンストラクターが例外をスローするときに問題になります。できることは、using ブロックを try/catch ブロック内にラップすることだけです。そのようにしなければならない理由はここにあります。

using ブロックは単なるシンタックス シュガーであり、コンパイラは各 using ブロックを同等の try/finall ブロッ​​クに置き換えます。唯一の問題は、コンパイラがコンストラクターを try ブロック内にラップしないことです。コンパイル後のコードは、IL で次の変換を行います。

        //Declare object x of type SomeClass.
        SomeClass x;

        //Instantiate the object by calling the constructor.
        x = new SomeClass("c:/temp/test.txt");

        try
        {
            //Do some work on x.
        }
        finally
        {
            if(x != null)
                x.Dispose();
        }

コードからわかるように、コンストラクターが例外をスローした場合、オブジェクト x はインスタンス化されず、処理されない場合、コントロールは例外発生のポイントからそれ以上移動しません。

昨夜、この件に関するブログ投稿をブログに投稿しました

なぜ C# の設計者がオブジェクトの構築を try ブロック内にラップしなかったのか、私はそうすべきだと思っています。

編集

C# がオブジェクト構築を using ブロックの代わりに生成された try ブロックにラップしない理由を見つけたと思います。

理由は簡単です。宣言とインスタンス化の両方を try ブロック内でラップするthe object would be out of scope for the proceeding finally blockと、コードはコンパイルされません。なぜなら、finally ブロックではオブジェクトがほとんど存在しないからです。構造を try ブロックでラップし、try ブロックの前に宣言を保持するだけの場合、その場合でも、 が見つかるためコンパイルされませんyou're trying to use an assigned variable

于 2010-07-29T09:13:37.030 に答える
1

これを確認するために簡単なテスト プログラムを一緒に投げましたが、コンストラクターで例外がスローされたときに Dispose メソッドが呼び出されないようです。

class Program
{
    static void Main(string[] args)
    {
        try
        {
            using (OtherClass inner = new OtherClass())
            {
                Console.WriteLine("Everything is fine");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        Console.Read();
    }
}

class OtherClass : IDisposable
{
    public OtherClass()
    {
        throw new Exception("Some Error!");
    }

    void IDisposable.Dispose()
    {
        Console.WriteLine("I've disposed my resources");
    }
}

出力:

いくつかのエラー!

例外をスローしない場合..

出力:

すべて順調

リソースを処分しました

おそらくこれは、オブジェクトが作成されていないためであり、Dispose を呼び出すものは何もありません。

コンストラクターが、通常は Dispose による適切なクリーンアップを必要とするリソースを既に割り当てていて、その後例外が発生した場合にどうなるかはわかりません。

于 2010-07-29T09:13:08.907 に答える
0

これは、適切に設計されたクラスでは問題になりません。全体的な質問を覚えておいてください:

public class HoldsResources : IDisposable
{
    public HoldsResources()
    {
        // Maybe grab some resources here
        throw new Exception("whoops");
    }
}

using (HoldsResources hr = new HoldsResources())
{
}

HoldsResources問題は、コンストラクターが例外をスローする前に割り当てられたリソースをどうするかということです。

答えは、これらのリソースについて何もすべきではないということです。それはあなたの仕事ではありません。が資源を保持することが決定されたとき、それはHoldsResourcesそれらを適切に処分する義務をもたらしました. これは、コンストラクターでの try/catch/finally ブロックを意味IDisposableし、メソッドでそれらのリソースを破棄するための適切な実装を意味しますDispose

あなたの責任は、インスタンスを使い終わったときに、usingブロックを使用して彼のメソッドを呼び出すことです。Dispose他には何もありません。

于 2010-07-31T06:29:20.833 に答える
-1

ガベージ コレクションの対象外のリソースを ctor 内で把握した場合は、事態が悪化したときにそれらを確実に破棄する必要があります。

このサンプルは、何か問題が発生したときにリークを防止する ctor を示しています。ファクトリ メソッド内でディスポーザブルを割り当てる場合と同じルールが適用されます。

class Sample
{
  IDisposable DisposableField;

  ...

  public Sample()
  {
    var disposable = new SomeDisposableClass();
    try
    {
       DoSomething(disposable);
       DisposableField = disposable;
    }
    catch
    {
       // you have to dispose of it yourself, because
       // the exception will prevent your method/ctor from returning to the caller.
       disposable.Dispose();
       throw;
    }
  }
}

編集: サンプルをファクトリから ctor に変更する必要がありました。(コメントから判断します。)

もちろん、その理由は次のとおりです。ファクトリまたは ctor を呼び出すと、その結果のみを破棄できます。通話が終了したら、これまでのところすべて問題ないと想定する必要があります。

俳優や工場に電話するとき、とにかく手に入れることができないものを処分するために逆精神分析を行う必要はありません. 例外がスローされた場合、例外を再スローする前に半分割り当てられたものをすべてクリアするのは、ファクトリ/ctor の責任です。(今回は十分に精巧だったといいのですが...)

于 2010-07-29T09:20:11.287 に答える