一个使用块,在构造函数中有一个对象初始化块。

6
如果您在 using 语句 中从构造函数调用对象,则该对象会自动被包装在 try/catch 块中进行处理。这是构造函数中的对象初始化块。
但是,如果在同一语句中初始化成员类型,会发生什么?例如:
  class TypeThatThrowsAnException 
  {
       public TypeThatThrowsAnException() 
       {
           throw new Exception();
       }
  }

  using (SomeDisposable ds = new SomeDisposable() {
       MemberThatThrowsAnException m = new TypeThatThrowsAnException()
  })
  {
       // inside using statement
       ds.doSomething();
  }

MemberThatThrowsAnException在初始化SomeDisposable时抛出异常时会发生什么,即执行代码块时会发生什么?

如果我们在using块的范围之外调用这些成员构造函数,是否会有任何区别?

  class TypeThatThrowsAnException 
  {
       public TypeThatThrowsAnException() 
       {
           throw new Exception();
       }
  }

  class SomeClass 
  {
       public static TypeThatThrowsAnException StaticMember 
       {
          get
          {
               return new TypeThatThrowsAnException();
          }
       }
  } 

  using (SomeDisposable ds = new SomeDisposable() {
       MemberThatThrowsAnException = SomeClass.StaticMember
  })
  {
       // inside using statement
       ds.doSomething();
  }

在某些情况下,这种写法可能很好并且易读,但我想知道是否存在任何警告或陷阱。或者这完全是行不通的。此外,您需要考虑可读性。


1
你是不是想要一个using语句,然后再加一个单独的代码块?你的代码目前不是很清晰。 - Jon Skeet
1
@Jon:我相信OP的意思是在构造函数语句中有一个using块,并带有一个对象初始化器块。我也被两对花括号搞糊涂了:P - Tomas Aschan
@Tomas:是的,我之前没有好好解决它。现在我解决了 :) - Jon Skeet
1
我现在已经知道可读性是主要问题了,不过... ;) - Caspar Kleijne
@Caspar:看起来你正在尝试在对象初始化器中声明一个新的变量(m),这并没有帮助。当前发布的代码无法编译。 - Jon Skeet
@Jon,谢谢你移除了m,(应该在VS中完成这个操作,而不是在这里直接输入(会很乱的;) - Caspar Kleijne
3个回答

5

在这里,对象初始化器在某种意义上是一个误导...但它们是问题可避免的一个例子。

只有当资源获取表达式正常完成后,对象才会被using语句"保护"。换句话说,你的代码就像这样:

SomeDisposable tmp = new SomeDisposable();
tmp.MemberThatThrowsAnException = new TypeThatThrowsAnException();

using (SomeDisposable ds = tmp)
{
    // Stuff
}

这更明显有问题 :)

当然,解决方法是将属性内部分配到using语句中:

using (SomeDisposable ds = new SomeDisposable())
{
    MemberThatThrowsAnException = new TypeThatThrowsAnException();
    // Stuff
}

现在我们只依靠SomeDisposable的构造函数来清理自己,如果它最终抛出异常-这是一个更合理的要求。


也许我错了,但是 using 语句是通过 try/finally 实现的,所以即使在构造时(类型初始化程序)抛出异常,IDisposable.Dispose 方法也将始终被调用。我错了吗? - Matías Fidemraizer
我明白了,顺便说一下,那是笔误:我在谈论对象初始化器。 - Matías Fidemraizer
第一个片段 - tmp 的处理怎么样?它会被从 ds 中的引用处理吗?ds 也需要被处理吗? - anatol
@anatol:除非SomeDisposable是一个结构体,否则tmpds引用同一对象。无论该对象如何被处理(即通过哪个变量),重要的是它被处理了 - Jon Skeet
我是指你在评论中的陈述(句子,命题)。抱歉,我漏掉了“除非”。没关系。 - anatol
显示剩余3条评论

2

在Ayende的博客中找到这篇文章,它涉及到using语句中的对象初始化器,但似乎与你的问题相关。


1
从我所看到的,您的SomeDisposable类具有TypeThatThrowsAnException类型的属性,您在实例化SomeDisposable时进行了初始化 - 是吗?
使用()即Dispose模式是一种简写,实际上会发出以下内容:-
SomeDisposable ds = null;

try
{
    ds = new SomeDisposable();
}
finally
{
    if (ds != null)
        ds.Dispose();
}

因此,如果您的类型的构造函数引发异常,控制权将立即传递到 finally 块。


1
不,那不是正确的翻译 - 因为你把SomeDisposable构造函数调用放在了try块内部。它不是。它在try块之前。请参阅C# 4规范的第8.13节。 - Jon Skeet
好的,谢谢Jon。如果有人需要的话,这是链接 - http://www.microsoft.com/en-au/download/details.aspx?id=7029 - sh1rts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接