开发者

Why is it legal to pass "Me" ByRef in VB.NET?

开发者 https://www.devze.com 2023-01-18 15:47 出处:网络
I was shocked just a moment ago to discover that the following is legal (the C# equivalent is definitely not):

I was shocked just a moment ago to discover that the following is legal (the C# equivalent is definitely not):

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 Su开发者_运维百科b

    ''// 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). 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.

  1. Why is it legal to pass 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?


This behavior actually follows pretty directly from the Visual Basic specification.

11.4.3 Instance Expressions

An instance expression is the keyword Me, MyClass, or MyBase. 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

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. Upon return, no copy of the resulting value will take place since Me is not a variable.


It appears the compiler transforms "Me" into a variable which is then passed ByRef. If you compile your code, then open it with Reflector, you can see what's happening:

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. The "a" variable doesn't get touched because it's not even a part of the function.


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.

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. 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.

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. While that is huge frustration with the parenthesis, it is a very good thing with 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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号