【问题标题】:Using 'Using' for things other than resource disposal [duplicate]将“使用”用于资源处理以外的事情[重复]
【发布时间】:2013-05-29 13:31:13
【问题描述】:

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

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

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

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 语句的适当使用吗?

【问题讨论】:

  • 我不认为您发布的内容有什么问题,但是我认为通过为像您的示例这样简单的事情实现接口是矫枉过正和误导。
  • 我的理解是using 在其作用域结束时调用IDisposable.Dispose ()。我认为using 的这种用法没有任何问题,但它会是IDisposable 的“坏”用法吗?
  • @I4V 很好读,感谢发帖!

标签: c# using-statement


【解决方案1】:

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

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

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

discussion linked in a comment by @I4V 有一些有趣的观点 - 包括反对这种来自无处不在的 Jon Skeet 的“虐待”的论点。

【讨论】:

  • 我同意 Jon 的声明,即避免在公开暴露的项目中使用它,而它可能会在内部使用,这往往是我们的平衡。
  • @AdamHouldsworth,在链接讨论中,我也对 Jon Skeet 的一些 cmets 表示同情。但是现在在ASP.NET MVC等主流库中都可以找到这样的滥用,我不禁觉得这个精灵已经开瓶了。
  • 确实,但是随着 .NET、LINQ 和吹捧流畅风格的扩展方法的日子一天天过去,拥有漂亮代码而不是略显丑陋但功能健全的代码的冲动对我来说是一个杀手,有时会编写这样的代码,这是一种绝对的意志力。谁知道呢,这可能是开发人员在创建 .NET 库之前使用的语言的社会指标。我的个人背景非常有限,所以 C# 在很长一段时间内主要是我的主要语言。语言经验会影响编码风格,同伴也会影响,我认为这是一个风格问题。
【解决方案2】:

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

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

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

【讨论】:

  • 不确定我是否同意。 using 语句删除了一些没有描述任何有用的垃圾代码,而且您可以将它们堆叠起来。它们编译成的东西不仅仅是try/finally,但本质上这就是归结为。我将它们用于需要后续操作的操作。 Rx 在消息推送中对某些事情执行此操作,并且可以很好地工作。归根结底,这种事情完全是固执己见,但如果以一种或另一种方式去做,不会造成任何伤害。
  • @AdamHouldsworth 是的,它归结为偏好,最后using 并没有为您提供比try/finally 更多的东西,否则我很乐意受到启发。恕我直言,堆叠 usings 并没有给您任何特别的好处,一个可以(虽然会很恶心)堆栈 try/finally,或者使用一个 try/finally 在一个 finally 块中执行所有 Dispose 方法。我同意Dispose 只是一个方法和一个名字,你可以做你认为合适的事情,但这就像说 diesleep 只是文字,我们都可以同意转换它们的用法,并在他睡觉时说john fell to death :)
  • 幸运的是using 你实际上看不到Dispose。它只是节省了几行多余的代码,堆叠只是节省了几个括号和几行。如果它们是相同的类型,您甚至可以用逗号分隔单个 using 中的项目。对我来说,所有这一切都归结为代码的外观 - 如果你把它去掉,那么是的,using 超过 try-finallythis 实例中没有给你任何东西。但是,using 并没有被翻译成简单的try-finally,所以说它在所有情况下都没有给你任何东西是不公平的。
  • @Jason - “最终 using 并没有为您提供比 try/finally 更多的功能” - 增加的简洁性显然是有价值的,或者 using 语句不会在第一个引入地方。问题是是否应该偏离 using 语句的原始目的,对此我的回答是:这取决于...
  • @AdamHouldsworth 我还在学习,并且肯定想知道它真正翻译成什么,根据 MSFT,msdn.microsoft.com/en-us/library/yh598w02.aspxusing 被编译器翻译成 try/finally: )
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

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

    如果你想要这种行为然后实现事件并公开它们,调用代码可以订阅它们并管理游标,如果需要的话,否则游标应该可以通过通用参数管理,也许使用BeginEnd 方法(虽然这样的命名约定通常是为方法的异步实现保留的,但你明白了)——以这种方式进行黑客攻击实际上并没有给你带来任何好处。

    【讨论】:

      【解决方案5】:

      我认为使用 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 使用不同的存储库。读者读到的是“使用新的工作单元”,这很有意义。

      【讨论】:

        猜你喜欢
        • 2012-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-07
        • 2020-03-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多