为什么在 VB.NET 中传递“Me”ByRef 是合法的?

问题描述 投票:0回答: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. 我说这样做的行为被编译器覆盖是否正确?如果没有,我错过了什么?
vb.net byref this-pointer
4个回答
5
投票
此行为实际上非常直接地遵循 Visual Basic 规范。

11.4.3 实例表达式

实例表达式是关键字

Me

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

9.2.5.2 参考参数

如果传递给引用参数的变量类型与引用参数的类型不兼容,

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

(都是我的重点)

因此,编译器将创建一个分配给

Me

 值的临时变量,以作为 
ByRef
 参数传递。返回时,不会复制结果值,因为 
Me
 不是变量。


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”变量不会被触及,因为它甚至不是函数的一部分。


1
投票
这只是程序员可能犯的数千种“几乎错误”之一。事实上,MS 捕获了其中的大多数,有时我对出现的警告数量感到惊讶。

他们错过了这个。

至于为什么它没有改变‘我’,那真是太好了!当您使用“me”时,出于安全目的,它只会传递您正在使用的真实类的副本。如果这按照您希望的方式起作用,我们就会谈论“巨大”副作用。你天真地在你的类的方法中工作,而它们

BAM突然间你就进入了一个完全不同的对象!那太糟糕了!如果您打算这样做,您不妨编写一段 MS-Basic 行号代码,其中所有全局变量都是随机设置的,并且没有子函数/函数。 如果您在括号中传递参数,其工作方式是相同的。例如,这按预期工作:

Assign(Reference_I_Want_To_Set, New Assigner)

但这并没有改变任何事情:
Assign((Reference_I_Want_To_Set), New Assigner)

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


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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.