2

我注意到 .NET 中的一个非常奇怪的行为,我花了一点时间才弄清楚发生了什么,因为对我来说,这并不直观。假设我有以下课程:

Public Class TestClass
    Private Shared people As New Dictionary(Of Integer, Person)

    Class Person
        Public Property FirstName() As String
        Public Property LastName As String

        Public Sub SayMyName()
            MsgBox(FirstName & " " & LastName)
        End Sub

        Public Sub New(FirstName As String, LastName As String)
            Me.FirstName = FirstName
            Me.LastName = LastName
        End Sub
    End Class

    Shared Sub Test()
        Dim checkPerson As Person = Nothing

        With checkPerson
            If (Not people.TryGetValue(3, checkPerson)) Then checkPerson = New Person("Bob", "Allen")
            .SayMyName()
        End With
    End Sub

    Shared Sub New()
        people.Add(1, New Person("John", "Doe"))
        people.Add(2, New Person("Jane", "Smith"))
    End Sub
End Class

如果我调用TestClass.Test(),则调用会引发 System.NullReferenceException SayMyName()。如果我正在调试,则没有明显的空引用。我最终意识到这是因为当“with”块启动时引用为空,即使调试器显示它存在,CLR 仍将其视为空值。

显然,为了清晰起见,我的示例被简化了,但在我的真实代码中,在“with”块之外声明实例方式然后有条件地在块内分配它是有用的(即,如果之前没有在“With”之外分配它堵塞)。

有人对此有合理的解释吗?我知道我可以轻松解决这个问题,但我只是想知道人们对这种行为的想法或了解。

4

2 回答 2

6

With block工作原理是获取一个表达式并对其进行一次评估,然后允许您将其用于多个语句。

在你的情况下,在这里,当你写这个时:

With checkPerson
    If (Not people.TryGetValue(3, checkPerson)) Then checkPerson = New Person("Bob", "Allen")
    .SayMyName()
End With

表达式checkPerson第一行。这相当于做:

' Evaluate the expression
Dim temp = checkPerson

' Perform statements, substituting as required
If (Not people.TryGetValue(3, checkPerson)) Then checkPerson = New Person("Bob", "Allen")
temp.SayMyName()

这种行为是设计使然,旨在防止在表达式昂贵时必须多次评估表达式,即:

With SomeExpensiveFunctionThatReturnsAnObject()

它清楚地记录在With 文档的表达式部分:

计算结果为对象的表达式。表达式可以是任意复杂的并且只计算一次。该表达式可以计算为任何数据类型,包括基本类型。

于 2013-10-11T18:44:31.947 回答
3

With块捕获您传递给它的表达式的当前值,一次。(它将这个值存储在编译器生成的隐藏变量中)

如果它按照您希望的方式运行,编写With SomeSlowFunction()代码会为块中的每一行代码再次调用该函数。

于 2013-10-11T18:44:25.793 回答