With 语句/块
但是,据我了解,With New ... 构造将使对象在超出范围时将对象标记为准备好进行垃圾回收。
这是真的,也不是真的。从某种意义上说,所有对象都被“标记”(纯粹主义者可能会对此术语提出质疑,但细节无关紧要)当它们超出范围时准备好进行垃圾收集,这是正确的。但从这个意义上说,它也不完全正确,因为With 关键字在这种行为方面并没有什么特别之处。当一个对象超出范围时,它就有资格进行垃圾回收。时期。这对于方法级范围是正确的,对于块级范围也是如此(例如,With、For、Using 等)。
但这不是您使用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 的对象时它非常重要。您绝对应该返回并重新检查您的代码,以确保您在适当的地方使用它!