Home All Groups Group Topic Archive Search About

Structures and Delegates and ByRef Arguments

Author
13 Nov 2007 4:56 AM
eBob.com
If I have a structure and I pass it ByRef to a Subroutine via a Delegate,
any changes to the structure are not seen by the caller.  This sure seems
unfortunate to me and I would welcome any explanation of why this is
reasonable behavior.  In any event I think that the Delegate documentation
should point this out in bold face type.  The following code illustrates the
problem:

Option Explicit On
Option Strict On

Public Class Form1
    Inherits System.Windows.Forms.Form

    Delegate Sub delegTestSub(ByRef si As SiteRunOpts)

    Structure SiteRunOpts
        Public set_by_Load_code As String
        Public set_by_TestSub_code As String
    End Structure

    Public Shared Sites(0) As SiteRunOpts

    Public onesite As SiteRunOpts

#Region " Windows Form Designer generated code "
#End Region

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load

        onesite = Sites(0)
        onesite.set_by_Load_code = "SET"

        Dim CallDaSub As delegTestSub = New delegTestSub(AddressOf testsub)

        Me.Invoke(CallDaSub, New Object() {onesite})
        '
        ''> onesite.set_by_TestSub is Nothing here !?!?!?!
        '
    End Sub

    Private Sub testsub(ByRef site As SiteRunOpts)
        site.set_by_TestSub_code = "SET"
    End Sub
End Class

(You have to use the debugger to see what I am saying.  Also, sorry if this
gets posted twice; I am having a problem with OE.)

Bob

Author
13 Nov 2007 5:43 AM
Tom Shelton
Show quote Hide quote
On Nov 12, 9:56 pm, "eBob.com" <eBob.***@totallybogus.com> wrote:
> If I have a structure and I pass it ByRef to a Subroutine via a Delegate,
> any changes to the structure are not seen by the caller.  This sure seems
> unfortunate to me and I would welcome any explanation of why this is
> reasonable behavior.  In any event I think that the Delegate documentation
> should point this out in bold face type.  The following code illustrates the
> problem:
>
> Option Explicit On
> Option Strict On
>
> Public Class Form1
>     Inherits System.Windows.Forms.Form
>
>     Delegate Sub delegTestSub(ByRef si As SiteRunOpts)
>
>     Structure SiteRunOpts
>         Public set_by_Load_code As String
>         Public set_by_TestSub_code As String
>     End Structure
>
>     Public Shared Sites(0) As SiteRunOpts
>
>     Public onesite As SiteRunOpts
>
> #Region " Windows Form Designer generated code "
> #End Region
>
>     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
> System.EventArgs) Handles MyBase.Load
>
>         onesite = Sites(0)
>         onesite.set_by_Load_code = "SET"
>
>         Dim CallDaSub As delegTestSub = New delegTestSub(AddressOf testsub)
>
>         Me.Invoke(CallDaSub, New Object() {onesite})
>         '
>         ''> onesite.set_by_TestSub is Nothing here !?!?!?!
>         '
>     End Sub
>
>     Private Sub testsub(ByRef site As SiteRunOpts)
>         site.set_by_TestSub_code = "SET"
>     End Sub
> End Class
>
> (You have to use the debugger to see what I am saying.  Also, sorry if this
> gets posted twice; I am having a problem with OE.)
>
> Bob

Option Strict On
Option Explicit On

Imports System

Module Module1
    Sub Main()
        Dim tc As New TestClass()
        tc.TestIt()
        Console.WriteLine(tc.onesite.set_by_Load_code)
        Console.WriteLine(tc.onesite.set_by_TestSub_code)
    End Sub

    Class TestClass
        Delegate Sub delegTestSub(ByRef si As SiteRunOpts)


        Structure SiteRunOpts
            Public set_by_Load_code As String
            Public set_by_TestSub_code As String
        End Structure


        Public Shared Sites(0) As SiteRunOpts
        Public onesite As SiteRunOpts

        Public Sub New()
            onesite = Sites(0)
            onesite.set_by_Load_code = "SET"

        End Sub
        Public Sub TestIt()
            Dim CallDaSub As delegTestSub = New delegTestSub(AddressOf
testsub)
            CallDaSub.Invoke(onesite)
        End Sub

        Private Sub testsub(ByRef site As SiteRunOpts)
            site.set_by_TestSub_code = "SET"
        End Sub

    End Class
End Module

This works fine for me...  Or were you expecting your Sites(0) to be
updated as well?  Because that won't happen.  Structures are value
types, and when you assign them, a copy is made.  So, onesite does NOT
refer to Sites(0).  If you want that sort of behavior, then you need
to make your siteopts a class.

--
Tom Shelton
Author
13 Nov 2007 6:12 PM
eBob.com
Show quote Hide quote
"Tom Shelton" <tom_shel***@comcast.net> wrote in message
news:1194932607.834823.41940@o80g2000hse.googlegroups.com...
> On Nov 12, 9:56 pm, "eBob.com" <eBob.***@totallybogus.com> wrote:
>> If I have a structure and I pass it ByRef to a Subroutine via a Delegate,
>> any changes to the structure are not seen by the caller.  This sure seems
>> unfortunate to me and I would welcome any explanation of why this is
>> reasonable behavior.  In any event I think that the Delegate
>> documentation
>> should point this out in bold face type.  The following code illustrates
>> the
>> problem:
>>
>> Option Explicit On
>> Option Strict On
>>
>> Public Class Form1
>>     Inherits System.Windows.Forms.Form
>>
>>     Delegate Sub delegTestSub(ByRef si As SiteRunOpts)
>>
>>     Structure SiteRunOpts
>>         Public set_by_Load_code As String
>>         Public set_by_TestSub_code As String
>>     End Structure
>>
>>     Public Shared Sites(0) As SiteRunOpts
>>
>>     Public onesite As SiteRunOpts
>>
>> #Region " Windows Form Designer generated code "
>> #End Region
>>
>>     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
>> System.EventArgs) Handles MyBase.Load
>>
>>         onesite = Sites(0)
>>         onesite.set_by_Load_code = "SET"
>>
>>         Dim CallDaSub As delegTestSub = New delegTestSub(AddressOf
>> testsub)
>>
>>         Me.Invoke(CallDaSub, New Object() {onesite})
>>         '
>>         ''> onesite.set_by_TestSub is Nothing here !?!?!?!
>>         '
>>     End Sub
>>
>>     Private Sub testsub(ByRef site As SiteRunOpts)
>>         site.set_by_TestSub_code = "SET"
>>     End Sub
>> End Class
>>
>> (You have to use the debugger to see what I am saying.  Also, sorry if
>> this
>> gets posted twice; I am having a problem with OE.)
>>
>> Bob
>
> Option Strict On
> Option Explicit On
>
> Imports System
>
> Module Module1
>    Sub Main()
>        Dim tc As New TestClass()
>        tc.TestIt()
>        Console.WriteLine(tc.onesite.set_by_Load_code)
>        Console.WriteLine(tc.onesite.set_by_TestSub_code)
>    End Sub
>
>    Class TestClass
>        Delegate Sub delegTestSub(ByRef si As SiteRunOpts)
>
>
>        Structure SiteRunOpts
>            Public set_by_Load_code As String
>            Public set_by_TestSub_code As String
>        End Structure
>
>
>        Public Shared Sites(0) As SiteRunOpts
>        Public onesite As SiteRunOpts
>
>        Public Sub New()
>            onesite = Sites(0)
>            onesite.set_by_Load_code = "SET"
>
>        End Sub
>        Public Sub TestIt()
>            Dim CallDaSub As delegTestSub = New delegTestSub(AddressOf
> testsub)
>            CallDaSub.Invoke(onesite)
>        End Sub
>
>        Private Sub testsub(ByRef site As SiteRunOpts)
>            site.set_by_TestSub_code = "SET"
>        End Sub
>
>    End Class
> End Module
>
> This works fine for me...  Or were you expecting your Sites(0) to be
> updated as well?  Because that won't happen.  Structures are value
> types, and when you assign them, a copy is made.  So, onesite does NOT
> refer to Sites(0).  If you want that sort of behavior, then you need
> to make your siteopts a class.
>
> --
> Tom Shelton
>
Hi Tom,  As always thanks for your prompt and helpful response.  Your code
works fine for me too.  And no I was not expecting Sites(0) to be updated.
But I am still confused as to why, in my code, onesite is not updated.  It
is at this point academic as I have changed my structure to a class and
moved on.  But I sort of get into these issues.  I mean, here I am running
valid code, compiled with Strict On and Explicit On, and following all
relevant documentation which I have been able to find, and the code does not
work (IMHO).  Well, as I said I have moved on.  But if you or anyone else
looking at this has any idea why my code code does not work (i.e. the update
to onesite in testsub is not seen by the caller) I would sure be interested.
Thanks again,  Bob
Author
13 Nov 2007 8:32 PM
Tom Shelton
Show quote Hide quote
On Nov 13, 11:12 am, "eBob.com" <eBob.***@totallybogus.com> wrote:
> "Tom Shelton" <tom_shel***@comcast.net> wrote in message
>
> news:1194932607.834823.41940@o80g2000hse.googlegroups.com...
>
>
>
> > On Nov 12, 9:56 pm, "eBob.com" <eBob.***@totallybogus.com> wrote:
> >> If I have a structure and I pass it ByRef to a Subroutine via a Delegate,
> >> any changes to the structure are not seen by the caller.  This sure seems
> >> unfortunate to me and I would welcome any explanation of why this is
> >> reasonable behavior.  In any event I think that the Delegate
> >> documentation
> >> should point this out in bold face type.  The following code illustrates
> >> the
> >> problem:
>
> >> Option Explicit On
> >> Option Strict On
>
> >> Public Class Form1
> >>     Inherits System.Windows.Forms.Form
>
> >>     Delegate Sub delegTestSub(ByRef si As SiteRunOpts)
>
> >>     Structure SiteRunOpts
> >>         Public set_by_Load_code As String
> >>         Public set_by_TestSub_code As String
> >>     End Structure
>
> >>     Public Shared Sites(0) As SiteRunOpts
>
> >>     Public onesite As SiteRunOpts
>
> >> #Region " Windows Form Designer generated code "
> >> #End Region
>
> >>     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
> >> System.EventArgs) Handles MyBase.Load
>
> >>         onesite = Sites(0)
> >>         onesite.set_by_Load_code = "SET"
>
> >>         Dim CallDaSub As delegTestSub = New delegTestSub(AddressOf
> >> testsub)
>
> >>         Me.Invoke(CallDaSub, New Object() {onesite})
> >>         '
> >>         ''> onesite.set_by_TestSub is Nothing here !?!?!?!
> >>         '
> >>     End Sub
>
> >>     Private Sub testsub(ByRef site As SiteRunOpts)
> >>         site.set_by_TestSub_code = "SET"
> >>     End Sub
> >> End Class
>
> >> (You have to use the debugger to see what I am saying.  Also, sorry if
> >> this
> >> gets posted twice; I am having a problem with OE.)
>
> >> Bob
>
> > Option Strict On
> > Option Explicit On
>
> > Imports System
>
> > Module Module1
> >    Sub Main()
> >        Dim tc As New TestClass()
> >        tc.TestIt()
> >        Console.WriteLine(tc.onesite.set_by_Load_code)
> >        Console.WriteLine(tc.onesite.set_by_TestSub_code)
> >    End Sub
>
> >    Class TestClass
> >        Delegate Sub delegTestSub(ByRef si As SiteRunOpts)
>
> >        Structure SiteRunOpts
> >            Public set_by_Load_code As String
> >            Public set_by_TestSub_code As String
> >        End Structure
>
> >        Public Shared Sites(0) As SiteRunOpts
> >        Public onesite As SiteRunOpts
>
> >        Public Sub New()
> >            onesite = Sites(0)
> >            onesite.set_by_Load_code = "SET"
>
> >        End Sub
> >        Public Sub TestIt()
> >            Dim CallDaSub As delegTestSub = New delegTestSub(AddressOf
> > testsub)
> >            CallDaSub.Invoke(onesite)
> >        End Sub
>
> >        Private Sub testsub(ByRef site As SiteRunOpts)
> >            site.set_by_TestSub_code = "SET"
> >        End Sub
>
> >    End Class
> > End Module
>
> > This works fine for me...  Or were you expecting your Sites(0) to be
> > updated as well?  Because that won't happen.  Structures are value
> > types, and when you assign them, a copy is made.  So, onesite does NOT
> > refer to Sites(0).  If you want that sort of behavior, then you need
> > to make your siteopts a class.
>
> > --
> > Tom Shelton
>
> Hi Tom,  As always thanks for your prompt and helpful response.  Your code
> works fine for me too.  And no I was not expecting Sites(0) to be updated.
> But I am still confused as to why, in my code, onesite is not updated.  It
> is at this point academic as I have changed my structure to a class and
> moved on.  But I sort of get into these issues.  I mean, here I am running
> valid code, compiled with Strict On and Explicit On, and following all
> relevant documentation which I have been able to find, and the code does not
> work (IMHO).  Well, as I said I have moved on.  But if you or anyone else
> looking at this has any idea why my code code does not work (i.e. the update
> to onesite in testsub is not seen by the caller) I would sure be interested.
> Thanks again,  Bob- Hide quoted text -
>
> - Show quoted text -

I'm not sure... I restructed mine a little - just to be console app,
but other then that I pretty much just copied and pasted the relevant
parts of your code.  Maybe I'll try and run it as is latter.

--
Tom Shelton
Author
14 Nov 2007 4:51 AM
Tom Shelton
On Nov 13, 1:32 pm, Tom Shelton <tom_shel***@comcast.net> wrote:
> On Nov 13, 11:12 am, "eBob.com" <eBob.***@totallybogus.com> wrote:
>
>
>
>
>
> > "Tom Shelton" <tom_shel***@comcast.net> wrote in message
>

<snip>

> I'm not sure... I restructed mine a little - just to be console app,
> but other then that I pretty much just copied and pasted the relevant
> parts of your code.  Maybe I'll try and run it as is latter.
>
> --
> Tom Shelton- Hide quoted text -
>
> - Show quoted text -

Ok... I have it figured out.  The problem is Me.Invoke.  The parameter
is array of object - which means that a boxing operation occurs, which
does not occure with a reference type, causing a new copy to be made
of the object.  If you change the line to:

CallDaSub.Invoke(onesite)

Then everything works as expected.  Once I debuged through the code -
I realized what was going on :)

--
Tom Shelton
Author
14 Nov 2007 6:09 PM
eBob.com
Show quote Hide quote
"Tom Shelton" <tom_shel***@comcast.net> wrote in message
news:1195015884.819086.230170@d55g2000hsg.googlegroups.com...
> On Nov 13, 1:32 pm, Tom Shelton <tom_shel***@comcast.net> wrote:
>> On Nov 13, 11:12 am, "eBob.com" <eBob.***@totallybogus.com> wrote:
>>
>>
>>
>>
>>
>> > "Tom Shelton" <tom_shel***@comcast.net> wrote in message
>>
>
> <snip>
>
>> I'm not sure... I restructed mine a little - just to be console app,
>> but other then that I pretty much just copied and pasted the relevant
>> parts of your code.  Maybe I'll try and run it as is latter.
>>
>> --
>> Tom Shelton- Hide quoted text -
>>
>> - Show quoted text -
>
> Ok... I have it figured out.  The problem is Me.Invoke.  The parameter
> is array of object - which means that a boxing operation occurs, which
> does not occure with a reference type, causing a new copy to be made
> of the object.  If you change the line to:
>
> CallDaSub.Invoke(onesite)
>
> Then everything works as expected.  Once I debuged through the code -
> I realized what was going on :)
>
> --
> Tom Shelton
>
In the real application what I need to do is to add a control to the UI
form, and that of course has to happen on the thread associated with the UI
form.  So I think (and I am in over my head here) that I have to use the
form's Invoke method.  And the official doc I have found for using the
form's Invoke method says that the only way to pass arguments is to pass an
Object which is an array of objects.  So I think I have no choice about the
call to the form's Invoke method.  (Except, as we have discussed, that I
don't have to use a structure, I can use a class object, which is what I am
doing now.)  I hadn't encountered boxing before.  I just read a bit about it
in Balena and he refers to "unboxing" but does not say under what
circumstances it occurs.  Apparently not on the return from the Form Invoke
method.

Thanks very much for helping me to resolve this mystery.  I've learned a
lot.

Bob