8

我们都知道,using对于想要及时清理的资源,比如打开的文件或者数据库的连接,这个语句是非常有用的。

Dispose()我想知道在资源清理不是该方法的目标而是重置到以前的状态的情况下使用该语句是否被认为是一件好事。

例如,一个类允许 using 语句包装一个过程,该过程需要相当长的时间并将 Cursor 更改为等待状态。

class CursorHelper : IDisposable
{
   readonly Cursor _previousState;
   public CursorHelper(Cursor newState)
   {
      _previousState = Cursor.Current;
      Cursor.Current = newState;
   }

   public void Dispose()
   {
      Cursor.Current = _previousState;
   }
}

然后可以这样使用该类,而不必担心完成后恢复光标。

public void TimeIntensiveMethod()
{
   using (CursorHelper ch = new CursorHelper(Cursors.WaitCursor))
   {
      // something that takes a long time to complete
   }
}

这是对using语句的适当使用吗?

4

5 回答 5

5

实际上using只是语法糖try/finally,所以你为什么不像下面那样做简单的旧尝试/最后......

try
{
    // do some work
}
finally
{
    // reset to some previous state
}

恕我直言,实现Dispose方法以重置为某种状态会非常具有误导性,尤其是如果您的代码有消费者。

于 2013-05-29T13:34:27.477 回答
5

以这种方式(ab)使用using语句肯定有先例,例如ASP.NET MVC 框架中的FormExtensions.BeginForm。这会在释放时呈现一个<form>结束标记,其主要目的是在 MVC 视图中启用更简洁的语法。即使抛出异常,该Dispose方法也会尝试呈现结束标记,这有点奇怪:如果在呈现表单时抛出异常,您可能不想尝试呈现结束标记。

另一个例子是 log4net 框架中的(现已弃用的)NDC.Push方法,它返回一个IDisposable其目的是弹出上下文的方法。

一些纯粹主义者会说这是一种滥用,我建议您根据具体情况做出自己的判断。就个人而言,我认为您渲染沙漏光标的示例没有任何问题。

@I4V 在评论中链接的讨论有一些有趣的观点——包括反对这种来自无处不在的 Jon Skeet 的“虐待”的论点。

于 2013-05-29T13:34:40.273 回答
4

我反对这一点,并认为这是一种滥用。我还认为 C++ 中的 RAII 是一个糟糕的主意。我知道我在这两个职位上都是少数派。

这个问题是重复的。有关为什么我认为这是对 using 语句的无根据滥用的详细原因,请参阅:https ://stackoverflow.com/a/2103158/88656

于 2013-05-29T15:12:41.230 回答
2

不,不适合使用usingand/or Dispose。该模式有一个非常明确的用途(“定义释放分配资源的方法。”),不是这样。任何使用此代码的未来开发人员都会以对这种邪恶的蔑视来看待它。

如果你想要这种行为然后实现事件并暴露它们,调用代码可以订阅它们并管理游标,如果需要的话,否则游标应该可以通过通用参数管理,也许使用BeginEnd方法(尽管这样的命名约定通常是保留用于方法的异步实现,你得到了图片) - 以这种方式进行黑客攻击实际上并没有给你带来任何东西。

于 2013-05-29T13:48:15.760 回答
-1

我认为使用 using-Disposable 的方式不仅仅是处理对象是有意义的。当然,这取决于上下文和用法。如果导致代码更具可读性,则可以。

我曾在工作单元和存储库模式实现中使用它,例如:

public class UnitOfWork: IDisposable  {
    // this is thread-safe in actual implementation
    private static Stack<UnitOfWork> _uowStack = new Stack<UnitOfWork>();
    public static UnitOfWork Current {get { return _uowStack.Peek(); }} 

    public UnitOfWork() {
        _uowStack.Push(this);
    }

    public void Dispose() {
        _ouwStack.Pop();
    }

    public void SaveChanges() {
        // do some db operations
    }
}

public class Repository {
    public void DoSomething(Entity entity) {
        // Do Some db operations         

        UnitOfWork.Current.SaveChanges();
    }
}

通过这种实现,可以保证嵌套操作将使用它们对应的 UnitOfWork 而不传递参数。用法是一样的。

using (new UnitOfWork()) 
{
    var repo1 = new UserRepository();
    // Do some user operation

    using (new UnitOfWork())  
    {
         var repo2 = new AccountingRepository();
         // do some accounting
    }

    var repo3 = new CrmRepository();
    // do some crm operations
}

在此示例中,repo1 和 repo3 使用相同的 UnitOfWork,而 repo2 使用不同的存储库。读者阅读的是“使用新的工作单元”,这很有意义。

于 2013-05-29T13:48:30.997 回答