随着...结束与在VB.NET中使用

yu_*_*nae 10 vb.net scope idisposable using-statement with-statement

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

到现在为止,我认为它没有它(愚蠢的我,我知道......)并做了这样的事情:

With New OleDbConnection(MyConnectionString)
   ' Do stuff
End With
Run Code Online (Sandbox Code Playgroud)

与使用这样的using语句相比,这有什么影响

Using cn as New OleDBConnection(MyConnectionString)
    With cn
        ' Do stuff with cn
    End With
End using
Run Code Online (Sandbox Code Playgroud)

更新:

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

但是,据我所知,With New ...构造将使对象在对象超出范围时将其标记为垃圾收集.

所以我的问题是,唯一的区别在于using我将立即释放内存,而对于With构造,只要GC感觉它就会被释放吗?或者我错过了更大的东西?

是否有最佳实践意义?我应该去重写所有的代码我用With New MyDisposableObject() ... End WithUsing o as New MyDisposableObject()

Cod*_*ray 15

With 声明/块

但是,据我所知,With New ...构造将使对象在对象超出范围时将其标记为垃圾收集.

这既是真实的,也不是真的.从某种意义上说,所有对象都被"标记"(纯粹主义者可能会用这个术语狡辩,但细节不相关),因为它们在超出范围时已准备好进行垃圾收集.但从那时起,它也不完全正确,因为With关于这种行为的关键字并没有什么特别之处.当对象超出范围时,它有资格进行垃圾回收.期.这对方法级范围真和它的块级范围真(例如,With,For,Using,等等).

但这不是你使用的原因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
Run Code Online (Sandbox Code Playgroud)

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

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

但是,此处的效果与以下代码相同:

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
Run Code Online (Sandbox Code Playgroud)

当然,您不会引入新的范围,因此tempObj在更高级别的范围结束之前不会超出范围(因此有资格进行垃圾收集),但这几乎不是一个相关问题.性能增益(如果有的话)附加到后两个代码片段.

如今使用块的真正胜利With不是性能,而是可读性.有关更多想法With,可能的性能改进,样式建议等,请参阅此问题的答案.

With New

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

似乎唯一的目的With New是它允许你避免显式变量声明,而不是让编译器隐式地执行它.叫我疯了,但我认为没有优势.

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

Using 声明/块

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

顺便说一下,当一个对象有一个Dispose方法时,你应该总是遵循一条规则:你应该在你完成对象使用时总是调用它.如果你不这样做,它不一定是世界的尽头 - 垃圾收集器可能会保存你的培根 - 但它是记录合同的一部分,并且总是很好的做法,你需要调用Dispose提供它的每个对象.

如果您尝试包装IDisposableUsing块中实现的对象的创建,编译器将咆哮您并生成错误.它对任何其他类型没有意义,因为它的功能基本上等同于Try/ Finallyblock:

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
Run Code Online (Sandbox Code Playgroud)

但是当你开始嵌套时,这很难看并且变得相当笨拙(就像我们创建了一个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!]
Run Code Online (Sandbox Code Playgroud)

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

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


Jac*_*nev 9

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

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

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

  • @yu_ominae:托管资源是一个对象,它要求某个外部实体代表它做某事直到另行通知,已经请求垃圾收集器通知它是否被放弃,如果给出这样的通知将反过来告诉另一个实体它的服务不再需要.非托管资源是与外部实体的协议,代表对象做某事直至另行通知,并结合对象承诺发送此类通知,但没有任何方法确保在放弃该对象时通知将被交付. (2认同)