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 块只是语法糖,编译器用等效的 try/finall 块替换每个 using 块。唯一的问题是编译器没有将构造函数包装在 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# 不将对象构造包装到生成的 try 块中代替 using 块的答案。

原因很简单。如果您将声明和实例化都包装在 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 回答