何时应该在VB6中使用With-End With和普通对象引用?

3

浏览我维护的代码时,我发现有些地方使用了 With - End With 结构...

With my_object
    .do_this()
    .do_that()
    .do_the_other()
End With

有时候更为直接明了。
my_object.do_this()
my_object.do_that()
my_object.do_the_other()

这两种形式之间有什么细微差别吗?一般来说,应该选择哪个?
(个人观点是我选择第二个,因为在第一个形式嵌套两三层后,它开始让我头疼 - 这是一个足够充分的理由吗?)
4个回答

4

如果对象引用实际上是更复杂的表达式,如属性获取器或函数的返回值,则会有所区别。

对比一下:

With MyObjectFactory.CreateMyObject()
    .do_this
    .do_that
    .WriteToDatabase
End With

针对明显错误的情况:

MyObjectFactory.CreateMyObject().do_this
MyObjectFactory.CreateMyObject().do_that
MyObjectFactory.CreateMyObject().WriteToDatabase

在这种情况下,实际的等效方法是创建一个引用:

Dim myObject as MyObject
Set myObject = MyObjectFactory.CreateMyObject() 
myObject.do_this
myObject.do_that
myObject.WriteToDatabase

关于是否应该使用With块,这实际上是个人喜好的问题。像您一样,我也会发现许多嵌套的With块令人困惑。这很可能也表明该函数应该拆分成多个函数。


1
我不认同“嵌套是不好的”这个论点。你可能会轻易地声称嵌套If块是你应该拆分过程的迹象。荒谬。缓存的匿名引用也不会对你造成任何成本,而声明另一个引用变量则会占用你的模块和项目符号总数。这更多是关于你如何理解语言的问题,而新手会更喜欢每次使用对象进行超限定。 - Bob77
@Bob - 就我个人而言,如果有太多嵌套的With块(像OP说的超过2或3个),我会感到困惑。嵌套的if语句也是同样的情况--一旦它们嵌套得太深,重构就变得困难和令人困惑。 - Justin
@Bob - 很高兴知道引用变量会计入符号总数 - 我甚至从未想过有限制。快速搜索显示它大约为32k个符号(来源)。 - Justin
深度缩进很快就会成为一个问题,这是毋庸置疑的。无论您嵌套哪个块结构:Ifs、Select Cases、Withs、Fors等,都没有关系。 - Bob77
Justin,使用With..End With的主要目的是只进行一次对象重新限定,从而提高性能。请看我的答案。 - AngryHacker

2

贾斯汀是错误的。 With...End With 结构不仅仅是语法糖,它还是一种性能技巧。当你有一个包含多个点(.)的对象路径时,性能提升非常明显,特别是在循环和/或处理Types(结构体)时。

例如,这段代码:

For x = 1 to my_object.Employee.Records.Count
    Debug.Print my_object.Employee.Records(x).ID
Next

将会更快,如下所示:
For x = 1 to my_object.Employee.Records.Count
    With my_object.Employee.Records(x)
        Debug.Print .ID
    End With
Next

正如@wqw所指出的那样,这种方式可能会更快(取决于您需要访问多少属性),因为它提供了最少的对象重新资格认证:

With my_object.Employee.Records
    For x = 1 to .Count
        Debug.Print Item(x).ID
    Next    
End With

尝试一下,你会看到不同之处。

更像是在循环外使用 With my_object.Employee.Records,然后在循环内使用 Debug.Print .Item(x).ID - wqw
你是指第一个还是第二个会更快一些?(对不起,我不确定“much faster as” 是指“比... 快得多” 还是 “改为... 之后更快”。)如果第二个更快,那的确很有趣——你知道它为什么更快吗? - Justin
@Justin,第二个要快得多。 - AngryHacker

1

新的回答只是为了发布代码。

请注意,使用匿名 With-cache 或显式引用变量(或过程参数)时,对象缓存并不总是按您的预期工作。下面的 DumpRS 和 DumpRSII 均执行相同的操作,即打印 RS 中的所有值:

Option Explicit
'Add a reference to ADO 2.5 or later.

Private RS As ADODB.Recordset

Private Sub MakeRS()
    Dim I As Integer

    Set RS = New ADODB.Recordset
    With RS
        .CursorLocation = adUseClient
        .Fields.Append "SomeField", adInteger
        .Open
        For I = 1 To 10
            .AddNew Array(0), Array(I)
        Next
    End With
End Sub

Private Sub DumpRS()
    With RS.Fields(0)
        RS.MoveFirst
        Do Until RS.EOF
            Debug.Print .Value
            RS.MoveNext
        Loop
    End With
End Sub

Private Sub DumpRSII(ByVal Field As ADODB.Field)
    With RS
        .MoveFirst
        Do Until .EOF
            Debug.Print Field.Value
            .MoveNext
        Loop
    End With
End Sub

Private Sub Main()
    MakeRS
    DumpRS
    DumpRSII RS.Fields(0)
    RS.Close
End Sub

Field 对象只是光标上的一个窗口。缓存 Field 对象可以显著提高重复按行 ADO 操作的性能。


0

我会仅使用第一种版本来设置属性值,就像在C#中的初始化块一样。如果您正在调用方法等,则使用第二种形式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接