【问题标题】:Is abusing IDisposable to benefit from "using" statements considered harmful? [closed]是否滥用 IDisposable 从被认为有害的“使用”语句中获益? [关闭]
【发布时间】:2023-03-29 22:57:01
【问题描述】:

接口IDisposable的目的是有序释放非托管资源。它与 using 关键字密切相关,该关键字定义了一个作用域,在该作用域结束后,相关资源将被处置。

因为这种机制非常简洁,我一再想让类实现IDisposable,以便能够以不适合的方式滥用这种机制。例如,可以实现类来处理这样的嵌套上下文:

class Context : IDisposable
{
    // Put a new context onto the stack
    public static void PushContext() { ... }

    // Remove the topmost context from the stack
    private static void PopContext() { ... }

    // Retrieve the topmost context
    public static Context CurrentContext { get { ... } }

    // Disposing of a context pops it from the stack
    public void Dispose()
    {
        PopContext();
    }
}

调用代码中的用法可能如下所示:

using (Context.PushContext())
{
   DoContextualStuff(Context.CurrentContext);
} // <-- the context is popped upon leaving the block

(请注意,这只是一个例子,并不是这个问题的主题。)

Dispose() 在离开 using 语句的作用域时被调用这一事实也可以被利用来实现各种依赖于作用域的东西,例如计时器。这也可以通过使用try ... finally 构造来处理,但在这种情况下,程序员将不得不手动调用某些方法(例如Context.Pop),而using 构造可以为thon 执行此操作。

IDisposable 的这种用法与 as stated in the documentation 的预期用途不一致,但诱惑仍然存在。

是否有具体的理由来说明这是一个坏主意并永远打消我的幻想,例如垃圾收集的复杂性、异常处理等。或者我应该继续以这种方式滥用这种语言概念来放纵自己吗?

【问题讨论】:

  • 还有很多其他的概念可能会被误用,上面就是其中之一,唯一的就是可读性,记住你的代码应该是清晰的/可读的并且可以被其他人维护开发人员。
  • 我同意哈比卜。永远想一想从现在起 18 个月后将不得不使用您的代码的可怜的混蛋。那个混蛋很有可能就是你:-)
  • 这是一个主观问题,但您可以注意到 ASP.NET MVC 框架以这种方式滥用 IDisposable - 例如FormExtensions.BeginForm
  • 我已经编辑了这个问题,更清楚地表明我不是在寻找意见(我已经有一个:“这很好用!”),但出于具体原因,这种用法可能/将导致问题。

标签: c# .net idisposable


【解决方案1】:

所以在 asp.net MVC 视图中,我们看到以下构造:

using(Html.BeginForm())
{
    //some form elements
}

滥用?微软(间接)说不。

如果您的构造需要在完成后发生某些事情,IDisposable 通常可以很好地工作。我已经不止一次这样做了。

【讨论】:

  • 与事务范围类似。
  • 同意花费者。微软使用它的方式并不总是释放资源,所以我认为它是“允许的”。话虽如此,可读性应该是您关心的问题。我认为BeginForm 很清楚;从列表中弹出,不是那么多。
  • 如果using 行包含“Push”,我会说弹出非常明显。
  • 为什么不直接使用try/finally?它同样具有可读性。
  • @ChrisDunaway using 已经是 try/finally 周围的语法糖,但有一个方便的标准接口。没有IDisposable,调用者需要知道如何清理,而using 则将负担交给创建者。
【解决方案2】:

“这样使用是不是滥用IDisposable接口”?大概吧。

using 用作纯粹的“范围”构造是否会使代码的意图更明显并具有更好的可读性?当然。

后者对我来说胜过前者,所以我说使用它。

【讨论】:

  • 为什么不直接使用try/finally?它同样具有可读性。
  • @ChrisDunaway 在这种情况下我不同意,主要是因为(a)在这种情况下,你最终会得到一个空的finally 和(b)IMO,因为using 声明了范围内的意图比try/finally 更清楚。对我来说,using 说“我正在对这个对象做一些事情,并希望它之后消失”; try/finally 说“我可能在这里遇到异常,我想处理它”。
  • 我不得不不同意你的部分陈述。 try/finally(没有 catch 子句)并不暗示任何关于异常的事情。 finally 将有任何必要的清理代码,并说“我想做以下然后最后清理”。至少我是这么读的。
【解决方案3】:

您当然不会是第一个以这种方式“滥用” IDisposable 的人。正如StatsD.NET client 所展示的那样,我最喜欢的可能是在计时器中使用它:

using StatsdClient;
...
using (statsd.LogTiming( "site.db.fetchReport" ))
{
  // do some work
}
// At this point your latency has been sent to the server

事实上,我很确定微软自己在一些库中使用它。我的经验法则是 - 如果它提高了可读性,那就去吧。

【讨论】:

  • 为什么不直接使用try/finally?它同样具有可读性。
  • @ChrisDunaway 这里的重点是在 using 块内对操作进行计时,而不是防止异常。您可以将其阅读为using myTimer, do ...,并且通过这样做,将原本应该是三行代码(timer.Start、timer.Stop、timer.SendToServer)的内容压缩为(可以说)更具可读性的内容。
猜你喜欢
  • 1970-01-01
  • 2010-09-08
  • 2010-12-26
  • 2011-10-08
  • 2011-09-01
  • 1970-01-01
  • 2011-09-16
  • 2011-08-12
  • 2015-09-09
相关资源
最近更新 更多