在没有可处理的情况下,“使用”是否合适?


9

在C#中,using语句用于确定性地配置资源,而无需等待垃圾回收器。例如,它可以用于:

  • 处置SQL命令或连接,

  • 关闭流,释放基础源,例如文件,

  • 免费的GDI +元素,

  • 等等

我注意到,using在没有什么要处理的情况下,这种用法越来越多,但是对于调用者来说,编写一个using块而不是两个单独的命令更方便。

例子:

  • 由Stack Overflow团队编写的MiniProfilerusing用于表示要分析的块:

    using (profiler.Step("Name goes here"))
    {
        this.DoSomethingUseful(i - 1);
    }

    一种替代方法是具有两个块:

    var p = profiler.Start("Name goes here");
    this.DoSomethingUseful(i - 1);
    profiler.Stop(p);

    另一种方法是使用动作:

    profiler.Step("Name goes here", () => this.DoSomethingUseful(i - 1));
  • ASP.NET MVC还选择了using以下形式:

    <% using (Html.BeginForm())
       { %>
           <label for="firstName">Name:</label>
           <%= Html.TextBox("name")%>
           <input type="submit" value="Save" />    
    <% } %>

这样的用法合适吗?鉴于存在几个缺点,如何证明这一点:

  • 初学者会迷路,因为这种用法与书籍和语言规范中所解释的用法不符,

  • 该代码应具有表现力。在这里,表达性受到了损害,因为的适当用法using是为了表明背后存在诸如流,网络连接或数据库之类的资源,该资源应在不等待垃圾收集器的情况下被释放。


2
如果没有以下条件,则拥有两个单独的语句的方法会遇到与朴素的资源管理相同的问题usingprofiler.Stop(p)不能保证后一个语句(例如)在遇到异常和控制流时执行。

1
@delnan:这解释了为什么MiniProfiler避免使用第二种方法。但是在所有情况下都应避免使用第二个代码,因为它很难编写和维护。
Arseni Mourzenko 2013年



Answers:


7

您的最后一条陈述-“ use的适当用法是为了表明背后有一种资源,例如流,网络连接或数据库,应在不等待垃圾收集器的情况下将其释放”,这是不正确的,其原因是在IDisposable接口的文档中提供:http : //msdn.microsoft.com/zh-cn/library/system.idisposable.aspx

该接口的主要用途是释放非托管资源。

因此,如果您的班级使用了任何非托管资源,则何时可能不希望发生GC都无关紧要-与GC没有任何关系,因为未托管的资源不是GC(https:// stackoverflow。 com / questions / 3607213 / What-is-meant-by-managed-vs-unmanaged-resources-in-net)。

因此,“使用”的目的不是为了避免等待GC,这是迫使这些非托管资源的释放,现在,类的实例超出范围,它的Finalize被调用之前。这很重要,因为显而易见的原因-非托管资源可能依赖于其他非托管资源,并且如果以错误的顺序处理(或最终确定)它们,可能会发生不良事件。

因此,如果在using块中实例化的类使用任何非托管资源,那么答案是肯定的-适当。

请注意,IDisposable不是用于释放非托管资源的规定,但是-这只是主要目的。这可能是类的作者有一定的作用,他们要强制发生在特定的时间,并实现了IDisposable可能会得到这样的事情发生的一种方式,但是否这是一个很好的解决方案的情况下是不能够仅根据具体情况回答。

无论哪种情况,使用“ using”都意味着该类实现了IDisposable,因此它不违反代码的表达能力;实际上,这很清楚了发生了什么。


“此接口的主要用途是释放非托管资源。” 我说微软的文档很烂。也许这就是他们在FCL中使用它的方式,但是近十年来,应用程序开发人员已经将它用于多种用途-有些是出于很好的理由,有些是出于不太好的原因。但是引用他们那部分文档根本无法回答OP的问题。
Jesse C. Slicer

1
请注意我的倒数第二段- 主要目的不一定是唯一目的。IDisposable可以出于任何原因而实现,并且它提供了具有预期行为和使用方式的已知接口,因此,当您看到它时,您会知道在使实例超出范围之前还需要进行其他操作。关键是:如果实现IDisposable,则使用适当;其他一切都只是序言。
Maximus Minimus

1
您基本上是说目的不是避免等待GC,而是避免等待终结器。但我看不出有什么区别:finalizer由GC调用。
svick

GC以不确定的方式运行-您不知道何时调用它。GC也只是调用终结器,但它本身不做任何处理-实际资源本身不在GC的控制范围之内。使用using(1)可以确定对象的范围,并且(2)已知可以在已知的时间点进行处置-当它退出using块的范围时,未管理的资源就消失了(或者-最坏的情况是-处理了)转移到其他会破坏它的东西上)。真的,这只是挑剔。阅读文档,所有这些都不需要重复。
Maximus Minimus

8

使用using隐含Dispose()方法的存在。其他程序员将假定该方法存在于对象上。因此,如果物体不是一次性的,则不应using在其上使用。

代码清晰为准。忽略usingIDisposable在对象上实现。

MiniProfiler似乎using用作“围栏”正在分析的代码的机制。这有一些优点。据推测,MiniProfiler正在调用Dispose()以停止计时器,或者当MiniProfiler对象超出范围时计时器将停止。

通常,您需要using在需要自动完成某种类型的终结处理时调用。的文档html.BeginForm说明,当在using语句中使用该方法时,它将</form>using块的末尾呈现结束标记。

那并不一定意味着它仍然不是滥用。


4
到目前为止,很明显。问题是,什么时候(如果有的话)是否应该实施IDisposable/ Dispose()尽管OP所描述的内容没有什么可配置的?

2
我以为我说清楚了。有没有时是合适的时间。
罗伯特·哈维

1
我的观点是,这Disposeusing完全有意义的(无论是否合适)。OP的示例全部实现Dispose,不是吗?和IIUC一样,问题是这些类使用Dispose而不是其他方法(例如MiniProfiler)是否正确Stop()

1
是的,但这不是问题。问题是这样做是否是一个好主意。

2
@delnan:再次,我想我说清楚了。如果它使您的代码意图更清晰,那么这是个好主意。如果不是,则不是。
罗伯特·哈维

1

using错误和异常安全。它确保Dispose()不管程序员犯任何错误都将被调用。它不会干扰捕获或处理异常,但是Dispose()当引发异常时,该方法将在堆栈上递归执行。

实现IDispose但没有任何处理要处理的对象。充其量只是为了将来验证其设计。这样您就不必重构源代码,以备将来需要处理某些东西时使用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.