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
现在我们修改该代码以使用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
. 您绝对应该返回并重新检查您的代码,以确保您在适当的地方使用它!