10

我刚刚发现,和 C# 一样,VB.NET 也有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 Withas重写所有代码Using o as New MyDisposableObject()吗?

4

4 回答 4

16

With语句/块

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

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

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

也许一些代码有助于使事情更清楚。每当你看到一个点时,想想可能会很慢!

假设您从以下代码开始,从深度嵌套的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在更高级别的范围结束之前不会超出范围(因此有资格进行垃圾收集),但这几乎不是相关问题。性能增益(如果有的话)附加到后两个代码片段。

现在使用块的真正With胜利不是性能,而是可读性。有关With可能的性能改进、风格建议等的更多想法,请参阅此问题的答案

With New?

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

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

事实上,我可以说老实说我从未见过任何使用这种语法的实际代码。我在谷歌上唯一能找到的就是这样的废话Call无论如何都是一个更好的选择)。

Using语句/块

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

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

如果您尝试将IDisposable在块中实现的对象的创建包装起来Using,编译器将向您咆哮并生成错误。它对任何其他类型都没有意义,因为它的功能本质上等同于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. 您绝对应该返回并重新检查您的代码,以确保您在适当的地方使用它!

于 2013-03-21T03:29:46.470 回答
9

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

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

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

于 2013-03-21T02:38:07.850 回答
4

不同之处在于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在即时窗口中创建监视。对于未命名的对象,您不能。

于 2013-03-21T03:27:14.993 回答
0

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

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

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

于 2017-06-08T10:08:54.933 回答