Strange Behavior with ByRef and Me

  • Thread starter Thread starter John-Michael O'Brien
  • Start date Start date
J

John-Michael O'Brien

I've run into a rather interesting problem. I have a structure (holding
a 3D transform matrix, but that's not too important) that has all major
functions defined as both shared and unshared, with the unshared ones
using the Me keyword to fill in the implicit self reference.

The routines use byref to fill in the changes to the matrix as they go,
However, when called with the Me keyword they don't seem to actually
effect the target structure. They can be seen affecting the value of the
thing that was given as the parameter, but that doesn't seem to be
reflected in the actual structure that the Me is supposed to be referencing.

This leads me to believe that VB.Net uses a proxy structure for the Me
keyword. Is this true?

----------------------------------------------------

The example structure code that caused the symptoms:

Public Structure Matrix3D
'mRowColumn
Public m11 As Single
Public m12 As Single
Public m13 As Single
Public m14 As Single
Public m21 As Single
Public m22 As Single
Public m23 As Single
Public m24 As Single
Public m31 As Single
Public m32 As Single
Public m33 As Single
Public m34 As Single

.... More code here ...

Public Sub Translate(ByRef X As Single, ByRef Y As Single, ByRef Z
As Single)
Translate(Me, X, Y, Z)
End Sub

Public Shared Sub Translate(ByRef Target As Matrix3D, ByRef X As
Single, ByRef Y As Single, ByRef Z As Single)
With Target
.m14 += X
.m24 += Y
.m34 += Z
End With
End Sub

.... More code here ...

End Structure
 
Could you post the code that calls into the class, along with what you
actually get and what you want / expect ?
 
John-Michael O'Brien said:
I've run into a rather interesting problem. I have a structure (holding a
3D transform matrix, but that's not too important) that has all major
functions defined as both shared and unshared, with the unshared ones
using the Me keyword to fill in the implicit self reference.

The routines use byref to fill in the changes to the matrix as they go,
However, when called with the Me keyword they don't seem to actually
effect the target structure. They can be seen affecting the value of the
thing that was given as the parameter, but that doesn't seem to be
reflected in the actual structure that the Me is supposed to be
referencing.

This leads me to believe that VB.Net uses a proxy structure for the Me
keyword. Is this true?

----------------------------------------------------

The example structure code that caused the symptoms:

Public Structure Matrix3D
'mRowColumn
Public m11 As Single
Public m12 As Single
Public m13 As Single
Public m14 As Single
Public m21 As Single
Public m22 As Single
Public m23 As Single
Public m24 As Single
Public m31 As Single
Public m32 As Single
Public m33 As Single
Public m34 As Single

... More code here ...

Public Sub Translate(ByRef X As Single, ByRef Y As Single, ByRef Z As
Single)
Translate(Me, X, Y, Z)
End Sub

Public Shared Sub Translate(ByRef Target As Matrix3D, ByRef X As
Single, ByRef Y As Single, ByRef Z As Single)
With Target
.m14 += X
.m24 += Y
.m34 += Z
End With
End Sub

... More code here ...

End Structure


Good question. Possible reason: 'Me' is a reference to the boxed structure.
The procedure expects a reference to the unboxed object. That's obviously
not the same. Consequently the object (Me) is unboxed and the reference to
the unboxed object is passed to the procedure. Unboxing the object means you
get a copy and the copy will be modified, not the original. Just a guess.

Armin
 
Public Sub DoRender()
Dim FinalTransform as Matrix3D

'... Setup Code ...

FinalTransform.Translate(0, 0, 10)

'... Stuff that does work with Final Transform ...

End sub

FinalTransform's Initial State: (Manually Verified for certainty)
m11=1
m12=0
m13=0
m14=0
m21=0
m22=1
m23=0
m24=0
m31=0
m32=0
m33=1
m34=0

The expected final state is:
m11=1
m12=0
m13=0
m14=0
m21=0
m22=1
m23=0
m24=0
m31=0
m32=0
m33=1
m34=10

The actual final state is identical to the initial state.
 
John-Michael O'Brien,
Because Me is a readonly variable, ByRef states that the called function can
modify the variable of the calling fuction.

VB.NET "helps" you out in this situation by creating a dummy variable that
it puts the result of the ByRef in.

You really should only use ByRef when you want to change the variable that
is being passed, not the object that is being passed. At the very least all
your Single parameters should be ByVal.

I would code the methods the "other way around".

Public Sub Translate(ByVal X As Single, ByVal Y As Single, ByVal Z
As Single)
m14 += X
m24 += Y
m34 += Z
End Sub

Public Shared Sub Translate(ByRef Target As Matrix3D, ByVal X As
Single, ByVal Y As Single, ByVal Z As Single)
Target.Translate(X, Y, Z)
End Sub

Then I would seriously consider if the Shared Translate is really needed (I
don't see that it is).

NOTE: Because you made Matrix3D a Structure you need the ByRef on the Shared
Translate, however Matrix3D probably should be a class which will remove the
need to make Target ByRef. Structures are "best" reserved for types that:
- act like primitive types
- have an instance size under 16 bytes
- are immutable
- value semantics are desirable

http://msdn.microsoft.com/library/d...genref/html/cpconValueTypeUsageGuidelines.asp

Seeing as your Matrix3D has an instance size of 40 bytes or better and is
mutable, I would consider making it a Class instead of a Structure, then you
"eleminate" the above ByRef problem altogher...

Hope this helps
Jay

| I've run into a rather interesting problem. I have a structure (holding
| a 3D transform matrix, but that's not too important) that has all major
| functions defined as both shared and unshared, with the unshared ones
| using the Me keyword to fill in the implicit self reference.
|
| The routines use byref to fill in the changes to the matrix as they go,
| However, when called with the Me keyword they don't seem to actually
| effect the target structure. They can be seen affecting the value of the
| thing that was given as the parameter, but that doesn't seem to be
| reflected in the actual structure that the Me is supposed to be
referencing.
|
| This leads me to believe that VB.Net uses a proxy structure for the Me
| keyword. Is this true?
|
| ----------------------------------------------------
|
| The example structure code that caused the symptoms:
|
| Public Structure Matrix3D
| 'mRowColumn
| Public m11 As Single
| Public m12 As Single
| Public m13 As Single
| Public m14 As Single
| Public m21 As Single
| Public m22 As Single
| Public m23 As Single
| Public m24 As Single
| Public m31 As Single
| Public m32 As Single
| Public m33 As Single
| Public m34 As Single
|
| ... More code here ...
|
| Public Sub Translate(ByRef X As Single, ByRef Y As Single, ByRef Z
| As Single)
| Translate(Me, X, Y, Z)
| End Sub
|
| Public Shared Sub Translate(ByRef Target As Matrix3D, ByRef X As
| Single, ByRef Y As Single, ByRef Z As Single)
| With Target
| .m14 += X
| .m24 += Y
| .m34 += Z
| End With
| End Sub
|
| ... More code here ...
|
| End Structure
 
Thank you and Armin Zingler for your help.

The boxing thing actually makes a lot of sense given that Me is an
indirect object reference, which would imply boxing. So the changes I
was making would have been tossed out with the boxed copy of the variable.

As for the byref on the singles, I had originally done that because
passing byref is faster than byval in cases where the amount of data
being passed is smaller than the address bus width (Since you only have
to write down the address offset) but thinking a bit about it, singles
are 4 bytes anyway, and in .Net this advantage may not actually be
realized due to the increased type checking that goes with passing byref.

And I think your correction to the paired call methodology makes quite a
bit of sense, especially in the context of the boxing issue.
 
John,
| The boxing thing actually makes a lot of sense given that Me is an
| indirect object reference, which would imply boxing.

As far as I can tell there is *NO* boxing going on in your example! You can
use ILDASM to see if there are any box & unbox statements. The presence of
box & unbox IL statements would indicate boxing.

| So the changes I
| was making would have been tossed out with the boxed copy of the variable.
The changes you made would have been tossed out as Me is readonly. You
cannot do:

value = Me
Matrix3D value, x, y, z
Me = value

Which is effectively what the IL created is doing, however it skips the Me =
value, as Me is readonly. As you may realize passing a Property to a ByRef
parameter has a similar effect.


| As for the byref on the singles, I had originally done that because
| passing byref is faster than byval in cases where the amount of data
| being passed is smaller than the address bus width (Since you only have
IMHO Using ByRef as an optimization technique is a very poor idea! When
other developers read your code they will think that it is doing something
that it really isn't. Further the JIT compiler is smart enough to optimize
the code as it compiles it, if you try to "second guess" the (VB or JIT)
optimizer you are more then likely hurting the chance of the compilers to
create highly optimized code.

Remember the 80/20 rule. That is 80% of the execution time of your program
is spent in 20% of your code. I will optimize (worry about performance,
memory consumption) the 20% once that 20% has been identified & proven to be
a performance problem via profiling (CLR Profiler is one profiling tool).

For info on the 80/20 rule & optimizing only the 20% see Martin Fowler's
article "Yet Another Optimization Article" at
http://martinfowler.com/ieeeSoftware/yetOptimization.pdf

As I stated you should only use ByRef when you need ByRef, you only need
ByRef when the called routine needs to modify the actual variable of the
calling routine. In other words the called routine wants to return multiple
values to the calling routine.

Hope this helps
Jay


| Thank you and Armin Zingler for your help.
|
| The boxing thing actually makes a lot of sense given that Me is an
| indirect object reference, which would imply boxing. So the changes I
| was making would have been tossed out with the boxed copy of the variable.
|
| As for the byref on the singles, I had originally done that because
| passing byref is faster than byval in cases where the amount of data
| being passed is smaller than the address bus width (Since you only have
| to write down the address offset) but thinking a bit about it, singles
| are 4 bytes anyway, and in .Net this advantage may not actually be
| realized due to the increased type checking that goes with passing byref.
|
| And I think your correction to the paired call methodology makes quite a
| bit of sense, especially in the context of the boxing issue.
|
|
| Jay B. Harlow [MVP - Outlook] wrote:
| > John-Michael O'Brien,
| > Because Me is a readonly variable, ByRef states that the called function
can
| > modify the variable of the calling fuction.
| >
| > VB.NET "helps" you out in this situation by creating a dummy variable
that
| > it puts the result of the ByRef in.
| >
| > You really should only use ByRef when you want to change the variable
that
| > is being passed, not the object that is being passed. At the very least
all
| > your Single parameters should be ByVal.
| >
| > I would code the methods the "other way around".
| >
| > Public Sub Translate(ByVal X As Single, ByVal Y As Single, ByVal
Z
| > As Single)
| > m14 += X
| > m24 += Y
| > m34 += Z
| > End Sub
| >
| > Public Shared Sub Translate(ByRef Target As Matrix3D, ByVal X As
| > Single, ByVal Y As Single, ByVal Z As Single)
| > Target.Translate(X, Y, Z)
| > End Sub
| >
| > Then I would seriously consider if the Shared Translate is really needed
(I
| > don't see that it is).
| >
| > NOTE: Because you made Matrix3D a Structure you need the ByRef on the
Shared
| > Translate, however Matrix3D probably should be a class which will remove
the
| > need to make Target ByRef. Structures are "best" reserved for types
that:
| > - act like primitive types
| > - have an instance size under 16 bytes
| > - are immutable
| > - value semantics are desirable
| >
| >
http://msdn.microsoft.com/library/d...genref/html/cpconValueTypeUsageGuidelines.asp
| >
| > Seeing as your Matrix3D has an instance size of 40 bytes or better and
is
| > mutable, I would consider making it a Class instead of a Structure, then
you
| > "eleminate" the above ByRef problem altogher...
| >
| > Hope this helps
| > Jay
| >
| > | > | I've run into a rather interesting problem. I have a structure
(holding
| > | a 3D transform matrix, but that's not too important) that has all
major
| > | functions defined as both shared and unshared, with the unshared ones
| > | using the Me keyword to fill in the implicit self reference.
| > |
| > | The routines use byref to fill in the changes to the matrix as they
go,
| > | However, when called with the Me keyword they don't seem to actually
| > | effect the target structure. They can be seen affecting the value of
the
| > | thing that was given as the parameter, but that doesn't seem to be
| > | reflected in the actual structure that the Me is supposed to be
| > referencing.
| > |
| > | This leads me to believe that VB.Net uses a proxy structure for the Me
| > | keyword. Is this true?
| > |
| > | ----------------------------------------------------
| > |
| > | The example structure code that caused the symptoms:
| > |
| > | Public Structure Matrix3D
| > | 'mRowColumn
| > | Public m11 As Single
| > | Public m12 As Single
| > | Public m13 As Single
| > | Public m14 As Single
| > | Public m21 As Single
| > | Public m22 As Single
| > | Public m23 As Single
| > | Public m24 As Single
| > | Public m31 As Single
| > | Public m32 As Single
| > | Public m33 As Single
| > | Public m34 As Single
| > |
| > | ... More code here ...
| > |
| > | Public Sub Translate(ByRef X As Single, ByRef Y As Single, ByRef Z
| > | As Single)
| > | Translate(Me, X, Y, Z)
| > | End Sub
| > |
| > | Public Shared Sub Translate(ByRef Target As Matrix3D, ByRef X As
| > | Single, ByRef Y As Single, ByRef Z As Single)
| > | With Target
| > | .m14 += X
| > | .m24 += Y
| > | .m34 += Z
| > | End With
| > | End Sub
| > |
| > | ... More code here ...
| > |
| > | End Structure
| >
| >
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top