【问题标题】:With... End With vs Using in VB.NET用...结束与在 VB.NET 中使用
【发布时间】:2013-03-10 10:14:02
【问题描述】:

我刚刚发现,VB.NET 和 C# 一样,也有 using 关键字。

直到现在我还认为它没有它(我知道我真傻……),而是做了这样的事情:

With New OleDbConnection(MyConnectionString)
   ' Do stuff
End With

与使用这样的using 语句相比,这样做的含义是什么

Using cn as New OleDBConnection(MyConnectionString)
    With cn
        ' Do stuff with cn
    End With
End using

更新:

我应该补充一点,我熟悉 using 语句的作用,因为它会在结构退出时处理对象。

但是,据我了解,With New ... 构造将在对象超出范围时将对象标记为准备好进行垃圾回收。

所以我的问题真的是,唯一的区别是使用using 我将立即释放内存,而使用With 构造它会在GC 感觉时释放它?还是我在这里错过了更大的东西?

是否有任何最佳实践含义?我应该去重写所有我使用With New MyDisposableObject() ... End With作为Using o as New MyDisposableObject()的代码吗?

【问题讨论】:

  • 因此,如果从下面的答案中了解到,Using 唯一比With New ... 更好的是在处理非托管对象时。除非有人可以为我提供一个很好的例子,说明为什么不将 With New ... 用于本地一次性对象,否则我不明白为什么不应该使用它来保持代码整洁。

标签: vb.net scope idisposable using-statement with-statement


【解决方案1】:

通过使用With...End With,您可以对指定的对象执行一系列语句,而无需多次指定对象的名称。

Using 块的行为类似于 Try...Finally 构造,其中 Try 块使用资源,Finally 块处理它们。

托管资源由垃圾收集器处理,无需您进行任何额外编码。 您不需要 UsingWith 语句。 有时您的代码需要非托管资源。你负责他们的处置。 Using 块保证在您的代码使用完对象时调用对象上的 Dispose 方法。

【讨论】:

  • 这么说,你的意思是Using会调用非托管资源的Dispose()方法吗?我认为属于 .Net 框架的所有对象都得到了管理?
  • 非托管资源是:文件句柄、COM 包装器、数据库连接。声明实现所有资源清理逻辑的 Dispose 方法,编译器将在 Using 块中调用它。
  • 谢谢。因此,如果我正确理解了这一点,那么没有明确调用 Dispose() 的数据库连接将永远不会被垃圾收集?
  • @yu_ominae:托管资源是一个对象,它要求某个外部实体代表它做某事,直到另行通知,如果它被放弃,它会要求垃圾收集器通知,如果发出这样的通知将反过来告诉另一个实体不再需要它的服务。非托管资源是与外部实体达成的在另行通知之前代表对象执行某项操作的协议,并结合了对象提供此类通知的承诺,但没有任何方法确保在对象被放弃时会发送通知。
  • @supercat 谢谢,这是一个非常简洁的解释!
【解决方案2】:

区别在于Using With...End End

Using cn as New OleDBConnection(MyConnectionString)
    With cn
        ' Do stuff with cn
    End With
End using

超出范围时自动调用cn.Dispose() (End Using)。但是在With New...End

With New OleDbConnection(MyConnectionString)
    ' Do stuff
End With

.Dispose() 没有被显式调用。

同样使用命名对象,您可以在即时窗口中创建手表和?cn。对于未命名的对象,您不能。

【讨论】:

    【解决方案3】:

    With 语句/块

    但是,据我了解,With New ... 构造将使对象在超出范围时将对象标记为准备好进行垃圾回收。

    这是真的,也不是真的。从某种意义上说,所有对象都被“标记”(纯粹主义者可能会对此术语提出质疑,但细节无关紧要)当它们超出范围时准备好进行垃圾收集,这是正确的。但从这个意义上说,它也不完全正确,因为With 关键字在这种行为方面并没有什么特别之处。当一个对象超出范围时,它就有资格进行垃圾回收。时期。这对于方法级范围是正确的,对于块级范围也是如此(例如,WithForUsing 等)。

    但这不是您使用With 的原因。原因是它允许您在深度嵌套的对象上按顺序设置多个属性。换句话说,假设您有一个对象,您想要在其上设置一堆属性,并且您可以通过以下方式访问它:MyClass.MemberClass.AnotherMemberClass.Items(0)。看到所有这些点了吗?每次在对象上设置属性时,编写必须一遍又一遍地通过一系列点才能访问完全相同的对象的代码可能(理论上)变得低效。如果您对 C 或 C++(或任何其他具有指针的语言)有所了解,则可以将这些点中的每一个视为暗示指针取消引用。 With 语句基本上只通过一次所有间接寻址,storing the resulting object in a temporary variable,并允许您直接在存储在临时变量中的该对象上设置属性。

    也许一些代码可以让事情变得更清晰。每当您看到一个点时,就想可能会很慢!

    假设您从以下代码开始,从深度嵌套的 Items 集合中检索对象 1 并在其上设置多个属性。看看我们必须检索对象多少次,即使每次都是完全相同的对象?

    MyClass.MemberClass.AnotherMemberClass.Items(0).Color   = Blue
    MyClass.MemberClass.AnotherMemberClass.Items(0).Width   = 10
    MyClass.MemberClass.AnotherMemberClass.Items(0).Height  = 5
    MyClass.MemberClass.AnotherMemberClass.Items(0).Shape   = Circle
    MyClass.MemberClass.AnotherMemberClass.Items(0).Texture = Shiny
    MyClass.MemberClass.AnotherMemberClass.Items(0).Volume  = Loud
    

    现在我们修改该代码以使用With 块:

    With MyClass.MemberClass.AnotherMemberClass.Items(0)
        .Color   = Blue
        .Width   = 10
        .Height  = 5
        .Shape   = Circle
        .Texture = Shiny
        .Volume  = Loud
    End With
    

    不过这里的效果和下面的代码是一样的:

    Dim tempObj As MyObject = MyClass.MemberClass.AnotherMemberClass.Items(0)
    tempObj.Color   = Blue
    tempObj.Width   = 10
    tempObj.Height  = 5
    tempObj.Shape   = Circle
    tempObj.Texture = Shiny
    tempObj.Volume  = Loud
    

    当然,您不会引入新的范围,因此 tempObj 在更高级别的范围结束之前不会超出范围(因此有资格进行垃圾收集),但这几乎不是相关问题。后两个代码 sn-ps 的性能提升(如果有的话)。

    现在使用With 块的真正胜利不是性能,而是可读性。有关With 的更多想法、可能的性能改进、风格建议等,请参阅the answers to this question

    With New?

    With 语句中添加New 关键字与我们刚刚讨论的效果完全相同(创建一个局部临时变量来保存对象),只是它几乎完全没有意义。如果你需要用New创建一个对象,你不妨声明一个变量来保存它。稍后您可能需要对该对象一些操作,例如将其传递给另一个方法,而您不能在 With 块中这样做。

    似乎With New 的唯一目的是它允许您避免显式变量声明,而是让编译器隐式执行它。说我疯了,但我看不出这有什么好处。

    事实上,我可以说我真的从未见过任何使用这种语法的实际代码。我在 Google 上唯一能找到的是 nonsense like this(无论如何,Call 是一个更好的选择)。

    Using 语句/块

    With 不同,Using非常有用,并且应该经常出现在典型的 VB.NET 代码中。但是,它的适用性非常有限:它适用于类型实现IDisposable 接口模式的对象。您可以通过检查该对象是否有一个 Dispose 方法来判断这一点,当您完成它以释放任何非托管资源时,您应该调用该方法。

    顺便说一句,当对象具有Dispose 方法时,您应该始终遵循的规则:您应该始终在使用完对象时调用它。如果你不这样做,它不一定是世界末日——垃圾收集器可能会拯救你的培根——但它是记录在案的合同的一部分,并且始终是你为每个提供它的对象调用Dispose 的好习惯。

    如果您尝试在Using 块中包装一个实现IDisposable 的对象的创建,编译器将向您发出警告并生成错误。它对任何其他类型都没有意义,因为它的功能本质上等同于 Try/Finally 块:

    Try
        ' [1: Create/acquire the object]
        Dim g As Graphics = myForm.CreateGraphics()
        
        ' [2: Use the object]
        g.DrawLine(Pens.Blue, 10, 10, 100, 100)
        ' ... etc.
    End Try
    Finally
        ' [3: Ensure that the object gets disposed, no matter what!]
        g.Dispose()
    End Finally
    

    但是当你开始嵌套时,这很丑陋并且变得相当笨拙(就像我们创建了一个 Pen 对象,它也需要被释放)。相反,我们使用Using,效果相同:

    ' [1: Create/acquire the object]
    Using g As Graphics = myForm.CreateGraphics()
        ' [2: Use the object]
        g.DrawLine(Pens.Blue, 10, 10, 100, 100)
        ' ...etc.
    End Using  ' [3: Ensure that the object gets disposed, no matter what!]
    

    Using 语句既适用于您首先获取的对象(使用New 关键字或通过调用类似CreateGraphics 的方法),也适用于您已经创建的对象。在这两种情况下,它都能确保调用Dispose 方法,即使在块内的某个地方抛出异常,也能确保对象的非托管资源得到正确处置。

    我有点害怕你在 VB.NET 中编写代码不知道Using 语句。您不会将它用于创建所有对象,但在处理实现IDisposable 的对象时它非常重要。您绝对应该返回并重新检查您的代码,以确保您在适当的地方使用它!

    【讨论】:

    • 很好的答案,谢谢。我不同意您对With New 关键字的看法,它节省了声明一个变量,我认为当对象的使用仅限于方法并且只使用一次时,这很好。我现在应该用Using 重写所有内容,但是由于我总是使用With 然后Try Catch Finally 来处理我的对象,我认为我现在很好:)
    • 这是我如何使用With New stackoverflow.com/questions/15423898/… 的示例当然,使用Using 会更好看,但是您觉得这样做有什么严重错误吗?
    • 我同意@yu_ominae;能够直接转到With New MySubForm ... do stuff ... End With 真是太好了,因为一旦您有了对话结果,您就不会对子表单做太多事情。我喜欢With 块中看到的一件事是能够通过双点 (..) 或帽子 (^ )。
    【解决方案4】:

    使用连接...结束使用:小心!!! 此语句将关闭您的数据库连接

    在模块或表单的中间,即添加或更新记录,这将关闭连接。当您尝试在该模块中执行其他操作时,您将收到数据库错误。对于连接,我不再使用它。您可以使用 Try... End Try 而不使用 Using 语句。

    我在进入模块时打开连接并在退出时关闭它。这样就解决了问题。

    【讨论】:

    • 这不能回答问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-30
    • 1970-01-01
    • 2013-12-12
    • 2023-04-06
    • 2021-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多