vblang icon indicating copy to clipboard operation
vblang copied to clipboard

ByRef Me for Structure

Open rskar-git opened this issue 3 years ago • 3 comments

Per https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/program-structure/me-my-mybase-and-myclass : "Me behaves like either an object variable or a structure variable referring to the current instance."

However, the Me of a VB Structure will always be a copy of the instance when it's a parameter to a call, and never the instance itself, including where the call is for a ByRef parameter.

This proposal is to allow the expression ByRef Me to indicate when a reference to the instance is desired instead of its copy when targeting a ByRef parameter.

For example, this VB code, utilizing ByRef Me:

Module Program

    Structure PDQ
        Public P As Integer
        Public D As Decimal
        Public Q As Long
        Sub FooBar()
            Program.FooBar(ByRef Me)
        End Sub
    End Structure

    Sub FooBar(ByRef dst As PDQ)
        dst.P = 123
    End Sub

    Sub Main()
        Dim o As PDQ = Nothing
        o.FooBar()
        ' Prints 123 if ByRef Me is used; otherwise 0
        Console.WriteLine($"{o.P}")  
    End Sub

End Module

Would then work the same as ref this in this C# code:

class Program
{

    struct PDQ
    {
        public int P;
        public decimal D;
        public long Q;

        public void FooBar()
        {
            Program.FooBar(ref this);
        }
    }
    static void FooBar(ref PDQ dst)
    {
        dst.P = 123;
    }
    static void Main(string[] args)
    {
        PDQ o = default;
        o.FooBar();
        Console.WriteLine($"{o.P}"); // Prints 123
    }
}

rskar-git avatar Jan 15 '22 01:01 rskar-git

I like this. I previously proposed to allow us to optionally add ByRef to ref arguments for better clarity of code. But, I don't think both proposals will happen as they need language changes which is not valid now.

VBAndCs avatar Jan 15 '22 15:01 VBAndCs

I would argue that the underlying behavior is broken (or the documentation is, at the very least, misleading)...

Module ByrefTest

  Structure PDQ
    Public P As Integer
    Public D As Decimal
    Public Q As Long
    Sub FooBar()
      ByrefTest.FooBar(Me)
    End Sub
  End Structure

  Sub FooBar(ByRef dst As PDQ)
    dst.P = 123
  End Sub

  Sub Main()
    Dim o As PDQ = Nothing
    o.FooBar()
    ' Prints 0... expecting it to be 123.
    Console.WriteLine($"{o.P}")
  End Sub

End Module

I slightly modified the original code to reflect the point that, according to the documentation found at:

https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/modifiers/byref?f1url=%3FappId%3DDev16IDEF1%26l%3DEN-US%26k%3Dk(vb.ByRef);k(DevLang-VB)%26rd%3Dtrue

It states that the ByRef keyword "specifies that an argument is passed in such a way that the called procedure can change the value of a variable underlying the argument in the calling code."

I don't see the need to have to specify ByRef in both places. If the method specifies that it is ByRef that should be enough. If you wanted to force it to be ByVal (for whatever reason), we already have a mechanism in place to override that behavior. The problem as I see it is that the documentation suggests one thing but the implementation clearly doesn't follow what the docs specify.

DualBrain avatar Jan 18 '22 19:01 DualBrain

To further illustrate why I believe this is a bug...

Module ByrefTest

  Structure PDQ
    Public P As Integer
    Public D As Decimal
    Public Q As Long
    Sub FooBar()
      ByrefTest.FooBar(Me)
    End Sub
  End Structure

  Sub FooBar(ByRef dst As PDQ)
    dst.P = 123
  End Sub

  Sub Main1()
    Dim o As PDQ = Nothing
    o.FooBar() ' Call FooBar within the structure...
    Console.WriteLine($"{o.P}") ' Output: 0
    FooBar(o) ' Call FooBar outside of the structure...
    Console.WriteLine($"{o.P}") ' Output: 123
  End Sub

End Module

If I modify the sample so that FooBar is called slightly differently, I get two different results... event though I'm essentially executing the same code. The only difference is the usage of Me.

DualBrain avatar Jan 18 '22 19:01 DualBrain