C#中的using语句用法

8

关于using语句,我有一些疑问:

我有一个名为MyJob的类,该类是可释放的。然后,我还有一个在MyJob上也是可释放的属性JobResults。

我的代码:

using (MyJob job = new MyJob())
{
    //do something. 
     FormatResults(job.JobResults)
}

public string FormatResuls(JobResuts results)
{
}

我的第一个问题是:在使用块之后,MyJob和MyJob.Results是否都被处理或只有MyJob而不是MyJob.Results?

我还在使用C#中的任务进行并行处理:

Tasks allTasks = List<Tasks>

try
{
   foreach(Task t in allTasks)
   {
         // Each tasks makes use of an IDisposable object.
        t.Start();
   }
    Task.WaitAll(allTasks);
}
catch(AggregateExecption aex)
{
    /// How do I ensure that all IDisposables are disposed properly if     an exception happens in any of the tasks?
}

我的第二个问题是,在上面的代码中,处理任务异常时确保正确处理对象清理的正确方式是什么?

如果我的问题太幼稚或令人困惑,请原谅,因为我仍在学习和理解C#。感谢各位!


8
  1. 你提出了两个问题,这实际上不是[SO]推荐的格式。
  2. 关于第一个问题 - 我们不知道你的类是如何设计的。或者你是在问是否应该处理嵌套对象?
- Eugene Podskal
1
这取决于你的类MyJob如何实现IDisposable接口。 - Spencer Waz
这取决于你的类MyJob如何实现IDisposable接口。确保你遵循了正确的模式... 链接 - Spencer Waz
相关 - https://dev59.com/3U_Sa4cB1Zd3GeqP-j7N. - Eugene Podskal
我可能错了,但我相当确定using语句背后没有额外的魔法,除了所谓的“语法糖”之外,在执行封闭的代码块后调用IDisposable.Dispose参数。 - Grx70
4个回答

5
MyJob和MyJob.Results都被处理还是只有MyJob被处理而MyJob.Results不被处理?
这取决于您Dispose方法的实现方式。由于我们在您的问题中没有看到它,因此我假设您当前没有在MyJob.Dispose中处理您的Result属性,因此它将是后者。
由于只有MyJob包装在using语句中,并且再次假设它不会影响您的Result属性,因此它将被处理而Results不会被处理,因为它没有包装在using语句中。
您可以决定MyJob作为封装您的Result属性的对象也负责其处置。如果您决定如此,您可以在MyJobs.Dispose中处理Results。
如何确保正确处置任务异常处理中的对象?
如果传递给Task的委托包含在using语句中,则是安全的,因为using会将您的代码转换为try-finally块,如果在using块中发生异常,则finally块仍将运行yourObject.Dispose。

就像其他答案所说,如果他在MyJob.Dispose方法中调用Results.Dispose,那么它实际上会被处理掉,因此顶部部分只有在他没有在MyJob对象中显式处理它时才是真的。 - Ron Beyer
@RonBeyer 没错,我刚编辑了我的回答,就在你发表评论的时候 :) - Yuval Itzchakov
我认为“你可以决定”不是正确的措辞。该模式非常明确地说明,在各种Dispose()方法中,您必须处理可处置资源,这意味着MyJob必须处理任何它拥有的JobResult(在“具有-一个”关系中)。在实际文档中,没有任何解释的余地。 - pid
@pid 你是一名程序员,自我解释的空间总是存在的。虽然你说的是推荐的方法,但实现某些东西的方式并不唯一,他的使用情况可能不适合“具有”关系,所以让我们放弃它。例如,看看流包装器,比如GZipStream,它接受一个bool来指示是否处理底层流,或者保持它打开状态。 - Yuval Itzchakov

2
这里有很多好的答案,但有一点需要注意。如果您想确保您的对象被正确地处理,因为它使用不受管理的资源,您应该在您的对象中实现一个析构函数,例如:
class MyJob : IDisposable 
{
     private bool _disposed = false;
     protected virtual void Dispose(bool disposing)
     {
          if (!_disposed)
          {
              //Dispose unmanaged resources and any child disposables here
          }
     }

     void Dispose()
     {
          this.Dispose(true);
          GC.SuppressFinalize(this);
     }

     ~MyJob()
     {
          //ONLY use if you have unmanaged resources that DO NOT
          //implement their own finalizers.
          Dispose();
     }
}

然而,建议不要使用终结器(析构函数),除非您正在处理一种具有未托管资源的类型,并且该类型没有自己的终结器。

请参见https://msdn.microsoft.com/zh-cn/library/b1yfkh5e.aspx以获取实现IDisposable的最佳实践方法。


两件事。首先,我不建议实现终结器,特别是当我们不知道从OP的问题中是否涉及非托管资源时。其次,即使您最终实现了终结器,也应该在Dispose(bool disposing)方法内调用GC.SuppressFinalizer(this) - Yuval Itzchakov
@YuvalItzchakov,我编辑了一下,加入了suppress finalize调用,我之前漏掉了。我在帖子下面说了,除非满足某些特定的条件,否则不要实现它,而MSDN链接展示了很多好的实践方法。我只是假设未受管理资源没有自己的终结器,展示了一个完整的例子。 - Ron Beyer
别误会,我并不反对关于终结器问题的讨论,了解这样的事情很重要。我只是想说,考虑到问题中提供的细节很少,我不会着急去实现一个终结器 :) - Yuval Itzchakov

2

只有MyJobs被处理了。对于其他属性,您需要理解对象所有权:谁拥有该对象应该负责(通常)处理它。实现IDisposable接口的属性可能会被其他实例使用,因此只有在您是创建该属性的人且没有其他引用它的情况下,您才能将其处理掉,或者如果该类知道已被处理,则可以优雅地失败。


0

1) 最好的方法是在MyJob的Dispose()方法中处理JobResults。否则它不会自动释放。

2) 我会实现finally部分,遍历每个对象并将其释放。但在大多数情况下,您不需要这样做阅读此处


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