简体   繁体   English

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

[英]Why is it legal to pass “Me” ByRef in VB.NET?

I was shocked just a moment ago to discover that the following is legal (the C# equivalent is definitely not): 刚才我震惊地发现以下是合法的(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

But what's even stranger just as strange to me is that it doesn't seem to do what I expect: 但是,什么是 更奇怪 ,就像我奇怪的是,它似乎并没有什么,我希望:

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

a.AssignNew()

Console.WriteLine(a.GetField())

The above outputs "10," not "0" like I thought it would (though naturally, this expectation was itself infused with a certain kind of horror). 以上输出“10”,而非“0”,就像我认为的那样(虽然很自然,这种期望本身也注入了某种恐怖)。 So it seems that you can pass Me ByRef , but the behavior is somehow overridden (?) by the compiler to be as if you had passed Me ByVal . 因此,似乎您可以传递Me ByRef ,但编译器会以某种方式覆盖(?)行为,就好像您已经通过Me ByVal

  1. Why is it legal to pass Me ByRef ? 为什么通过Me ByRef是合法的? (Is there some backwards-compatibility explanation?) (是否有一些向后兼容性解释?)
  2. Am I correct in saying that the behavior of doing this is overridden by the compiler? 我是否正确地说编译器会覆盖执行此操作的行为? If not, what am I missing? 如果没有,我错过了什么?

It appears the compiler transforms "Me" into a variable which is then passed ByRef. 似乎编译器将“Me”转换为变量,然后传递ByRef。 If you compile your code, then open it with Reflector, you can see what's happening: 如果您编译代码,然后使用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

So it looks like when you call AssignNew(), you are assigning the new instance to the internally generated variable. 因此,当您调用AssignNew()时,您将新实例分配给内部生成的变量。 The "a" variable doesn't get touched because it's not even a part of the function. “a”变量不会被触及,因为它甚至不是函数的一部分。

This behavior actually follows pretty directly from the Visual Basic specification. 这种行为实际上直接来自Visual Basic规范。

11.4.3 Instance Expressions 11.4.3实例表达式

An instance expression is the keyword Me , MyClass , or MyBase . 实例表达式是关键字MeMyClassMyBase An instance expression, which may only be used within the body of a non-shared method, constructor, or property accessor, is classified as a value . 实例表达式(可能仅在非共享方法,构造函数或属性访问器的主体内使用)被归类为

9.2.5.2 Reference Parameters 9.2.5.2参考参数

If the type of the variable being passed to a reference parameter is not compatible with the reference parameter's type, or if a non-variable is passed as an argument to a reference parameter , a temporary variable may be allocated and passed to the reference parameter. 如果传递给引用参数的变量类型与引用参数的类型不兼容, 或者将非变量作为参数传递给引用参数 ,则可以分配临时变量并将其传递给引用参数。 The value being passed in will be copied into this temporary variable before the method is invoked and will be copied back to the original variable (if there is one) when the method returns. 传入的值将在调用方法之前复制到此临时变量中,并在方法返回时将其复制回原始变量(如果有)

(All emphasis mine) (都强调我的)

So, the compiler will create a temporary variable assigned to the value of Me to be passed as the ByRef parameter. 因此,编译器将创建一个临时变量,该变量分配给Me的值作为ByRef参数传递。 Upon return, no copy of the resulting value will take place since Me is not a variable. 返回时,由于Me不是变量,因此不会生成结果值的副本。

This is just one of the thousands of possible 'almost errors' a programmer can make. 这只是程序员可以做出的数千种“几乎错误”中的一种。 MS caught most of them, in fact, sometimes I'm suprised at how many warnings do come up. 事实上,MS抓住了他们中的大多数人,有时我会惊讶地发出了多少警告。

they missed this one. 他们错过了这个。

As far as why it doesn't change 'me', it's a darn good thing! 至于它为什么不改变'我',这是一件好事! When you use 'me', it just passes a copy of the real class you are working with, for safety purposes. 当您使用“我”时,为了安全起见,它只会传递您正在使用的真实班级的副本。 If this worked they way you were hoping, we would be talking GIANT side-effect. 如果按照你希望的方式工作,我们会谈论GIANT的副作用。 You're innocently working away with in your class' methods, and them BAM all of a sudden you are in an ENTIRELY different object! 你傻傻的工作走在你的类的方法,并把它们巴姆突然你在一个完全不同的对象! That would be awful! 那太可怕了! If you're going to do that, you might as well just write a piece of spagetti MS-Basic line-numbered code with all globals that get randomly set, and no subs/functions. 如果您要这样做,您可能只需编写一段spagetti MS-Basic行编号代码,其中包含随机设置的所有全局变量,并且没有子/函数。

The way this works is the same way if you pass arguments in parenthesis. 如果在括号中传递参数,它的工作方式是相同的。 For example this works as expected: 例如,这可以按预期工作:

Assign(Reference_I_Want_To_Set, New Assigner)

But this doesn't change anything: 但这并没有改变任何事情:

Assign((Reference_I_Want_To_Set), New Assigner)

If you reflect the above type of code as adam101 suggests you will see similar results. 如果您反映上述类型的代码,adam101建议您会看到类似的结果。 While that is huge frustration with the parenthesis, it is a very good thing with Me !!! 虽然这是对括号的巨大挫折,但对Me来说这是一件非常好的事情!

what you need to do to make this code work is this: 你需要做的是使这段代码工作是这样的:

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

then use it like 那就像使用它一样

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

    Assigner.AssignNew(a)

my understanding is you cannot change the reference of the object while using it, so you need to change it in a shared sub 我的理解是你在使用它时不能改变对象的引用,所以你需要在共享子中更改它


since Me cannot be the target of an assignment , the code seem to create a copy of it and from that point on, your not using the real object, but a copy of it 因为不能成为赋值的目标 ,所以代码似乎创建了它的副本,从那时起,你不使用真实对象,而是使用它的副本

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM