4

刚才我震惊地发现以下是合法的(C#等价物绝对不是):

Class Assigner
    ''// Ignore this for now.
    Public Field As Integer

    ''// This part is not so weird... take another instance ByRef,
    ''// assign it to a different instance -- stupid but whatever. '
    Sub Assign(ByRef x As Assigner, ByVal y As Assigner)
        x = y
    End Sub

    ''// But... what's this?!?
    Sub AssignNew()
        ''// Passing "Me" ByRef???
        Assign(Me, New Assigner)
    End Sub

    ''// This is just for testing.
    Function GetField() As Integer
        Return Me.Field
    End Function
End Class

但对我来说奇怪的是,它似乎并没有达到的预期:

Dim a As New Assigner With {.Field = 10}

a.AssignNew()

Console.WriteLine(a.GetField())

上面的输出是“10”,而不是我想象的“0”(当然,这种期望本身就充满了某种恐怖)。所以看起来你可以通过Me ByRef,但是编译器会以某种方式覆盖(?)行为,就好像你已经通过了Me ByVal

  1. 为什么通过是合法的Me ByRef(是否有一些向后兼容的解释?)
  2. 我是否正确地说这样做的行为被编译器覆盖了?如果没有,我错过了什么?
4

4 回答 4

5

看来编译器将“Me”转换为一个变量,然后通过 ByRef 传递。如果你编译你的代码,然后用 Reflector 打开它,你可以看到发生了什么:

Class Assigner
    ''// Methods
    Public Sub Assign(ByRef x As Assigner, ByVal y As Assigner)
        x = y
    End Sub

    Public Sub AssignNew()
        Dim VB$t_ref$S0 As Assigner = Me
        Me.Assign((VB$t_ref$S0), New Assigner)
    End Sub

    Public Function GetField() As Integer
        Return Me.Field
    End Function


    ''// Fields
    Public Field As Integer
End Class

所以看起来当您调用 AssignNew() 时,您正在将新实例分配给内部生成的变量。“a”变量没有被触及,因为它甚至不是函数的一部分。

于 2010-10-05T13:19:19.907 回答
5

这种行为实际上直接来自 Visual Basic 规范。

11.4.3 实例表达式

实例表达式是关键字Me,MyClassMyBase。只能在非共享方法、构造函数或属性访问器的主体中使用的实例表达式被归类为value

9.2.5.2 参考参数

如果传递给引用参数的变量的类型与引用参数的类型不兼容,或者如果将非变量作为参数传递给引用参数,则可以分配一个临时变量并将其传递给引用参数。传入的值将在调用方法之前复制到此临时变量中,并在方法返回时复制回原始变量(如果有的话)

(所有强调我的)

因此,编译器将创建一个临时变量,分配给Me要作为ByRef参数传递的值。返回时,不会复制结果值,因为Me它不是变量。

于 2010-10-05T14:47:39.963 回答
1

这只是程序员可能犯的数以千计的“几乎错误”之一。事实上,MS 抓住了他们中的大多数,有时我会惊讶于出现了多少警告。

他们错过了这个。

至于为什么它不改变“我”,这真是一件好事!当您使用“我”时,它只是传递您正在使用的真实课程的副本,以确保安全。如果这按您希望的方式工作,我们将谈论巨大的副作用。你在你班级的方法中天真地工作,他们突然间BAM你在一个完全不同的对象中!那将是可怕的!如果你打算这样做,你不妨写一段意大利面 MS-Basic 行编号代码,其中所有全局变量都是随机设置的,没有子/函数。

如果您在括号中传递参数,则其工作方式是相同的。例如,这按预期工作:

Assign(Reference_I_Want_To_Set, New Assigner)

但这并没有改变任何东西:

Assign((Reference_I_Want_To_Set), New Assigner)

如果您按照 adam101 的建议反映上述类型的代码,您将看到类似的结果。虽然这对括号很失望,但对Me!!!来说是一件非常好的事情

于 2010-10-05T13:36:36.883 回答
0

要使此代码正常工作,您需要做的是:

Class Assigner
''// Ignore this for now.

Private newPropertyValue As Integer
Public Property NewProperty() As Integer
    Get
        Return newPropertyValue
    End Get
    Set(ByVal value As Integer)
        newPropertyValue = value
    End Set
End Property


''// This part is not so weird... take another instance ByRef,
''// assign it to a different instance -- stupid but whatever. '
Shared Sub Assign(ByRef x As Assigner, ByVal y As Assigner)
    x = y
End Sub

''// But... what's this?!?
Shared Sub AssignNew(ByRef x As Assigner)
    ''// Passing "Me" ByRef???
    Assign(x, New Assigner)
End Sub
End Class

然后像使用它

    Dim a As New Assigner With {.NewProperty = 10}

    Assigner.AssignNew(a)

我的理解是你不能在使用它时更改对象的引用,所以你需要在共享子中更改它


由于Me不能成为 assignment 的目标,因此代码似乎创建了它的副本,从那时起,您不再使用真实对象,而是使用它的副本

于 2010-10-05T13:14:09.357 回答