簡體   English   中英

捕獲在Using塊的目標對象的構造函數中拋出的異常

[英]Catching exceptions thrown in the constructor of the target object of a Using block

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

在使用塊內,一切正常,正常處理異常。 但是,如果SomeClass的構造函數可以拋出異常怎么辦?

把你的使用放入try catch fe

try
{
   using(SomeClass x = new SomeClass("c:/temp/test.txt"))
   {
       ...
   }
}
catch(Exception ex)
{
   ...
}

是的 ,當構造函數拋出異常時,這將是一個問題。 您所能做的就是將try塊包裝在try / catch塊中。 這就是你必須這樣做的原因。

使用塊只是語法糖,編譯器使用等效的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

我一起扔了一個快速測試程序來檢查這個,似乎在構造函數中拋出異常時不會調用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進行適當的清理,然后發生異常,會發生什么。

對於精心設計的類,這不應該是一個問題。 記住整體問題:

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方法。 沒有其他的。

當您在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有責任清除半分配的任何東西。 (希望,這一次,它足夠精心......)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM