Boxing/Unboxing and ByRef stupidity..

cjard

Well-known member
Joined
Apr 25, 2006
Messages
7,081
Programming Experience
10+
Hi Guys..

Im having difficulty resolving a compiler warning (Option Strict is on) that says:
Warning 1 Implicit conversion from 'Object' to 'System.Windows.Forms.ToolStripButton' in copying the value of 'ByRef' parameter 'o' back to the matching argument.

I have this:
VB.NET:
Public Sub MySub(byref o as object)
  MyArrayList.Add(o)
End Sub

elsewhere i say:
VB.NET:
MySub(MyToolstripAddNewButton)

MyToolStripAddNewButton is a toolstrip button, and descended from object. the MyArrayList also contains normal Controls, and is iterated to enable or disable selected form elements at runtime. I have resolved the latebinding problems by inspecting the typeof at each iteration (may cause a performance hit - if it gets bad i might split the arraylist out into 2, one for controls and one for toolstripitems but for now, this is in place and it works..


So, my question is:

MyTolStripAddNewButton is being boxed into an object and passed by reference. I presume the error message comes when it is being unboxed again, but the issue i have is in the way vb seems to be handling the ByRef - it's literally making a new object, passing it in and then copying it back when it is done? This sounds a little dumb.. actually sounds a lot dumb, because there is then an implicit conversion that i cannot intervene on. My sub handles two objects with no common link in the hierarchy, so object is the only choice - how can i avoid suffering this compiler warning when my object is being copied instead of unboxed as the sub exits?
 
Why not just create an overloaded method that handles the other type you are using. I'm not entirely sure i understand what you mean when you say 'no common link in the hierachy' everything has common link in the hierachy, you are adding a toolstripbutton to an arraylist which inherits, i presume, from component, whats the other type you are trying to add to your arraylist. If they are two different types then i would be tempted to keep them in spearate arraylists anyway. Also why are you passing them Byref?


Edit: apologies just re-read you post, the two types are component and control.. So i would be tempted to keep them in separate arraylists. But i find myself asking the question, why? forms/containers already have a control collection, so why hold the whole control, not just a tag for example?
 
vis781 said:
you are adding a toolstripbutton to an arraylist which inherits, i presume, from component, whats the other type you are trying to add to your arraylist
Correct, one is a Control, one is a Component

If they are two different types then i would be tempted to keep them in spearate arraylists anyway.
Indeed, its a reasonalbe surmisation, but both these Types support the .Enabled property, which i'm using to switch on and off various text boxes for user input depending on whether the form is in add mode, browse mode, search mode, etc

Also why are you passing them Byref?
I'd like to pass everything ByRef - i'm a java guy who understands and appreciates the OO notion of passing a reference to an object instead of passing an entire object. Maybe i have a little skew in my VB thinking here as to what byval and byref mean, but i've looked at it, and i've even read in places that whether you write byref or byval, vb passes byref anyway. So my quandary is im sitting here thinking "i've told vb to pass byref. i heard that vb always passes byref, even when you say byval. but it looks like byref has some nasty behaviour somewhere where it needlessly copies a reference back when unboxing"
I'm very confused

Why i'm passing byref in this particular case, is that I already have 8 lists - one for each form mode, multiplied by true and false. I maintain a list of things on my form that i want dis/enabled in various modes, and so when the form enters search mode, the two lists of enableInSearchMode and disableInSearchMode are iterated, and every item in them is enabled or disabled respectively

I need to have lists that merely store references to the original objects, not keep whole new objects elsewhere, because if i do maintain a list that points to an object that is a New object, or a ByValled clone of a control, then i'm enabling and disabling an object that isnt necessarily on the form.. Does this make sense?
Suppose i said ByVal o as Object in MySub.. If VB was a good boy, then it would create a whole new clone copy of my object i pass in. the clone would go int he list, and I would have zero effect on the object visible on the form.

I'd have this:
[myarraylist(0)] -----pointer----> (new object in memory cloned from below)
myform.myTextBox-----pointer----->(control object on the form)

I want this:
[myarraylist(0)] -----pointer----> (form object on the form) <-----pointer------myform.myTextBox

And my understanding of byref/byval is that byval would(should, but i heard it wouldnt) realise the former, byref realises the latter.

Edit: apologies just re-read you post, the two types are component and control..
indeed.. annoyingly not inheriting from the same place.. yet annoying both having an enable property

So i would be tempted to keep them in separate arraylists.
i'm not quite ready to slam in another load of code changes to create 16 arraylists instead of 8, though i concede it would solve this particular problem.

But i find myself asking the question, why? forms/containers already have a control collection, so why hold the whole control, not just a tag for example?
Forms and containers do as you say, already have the .Controls collection, and man - what a nuisance that thing is. You see, my forms have several boxes of groupboxes, sometimes of groupboxes. Saying Me.Controls doesnt actually get you all the controls on the form, as there may be other controls within groupboxes. I wrote a recursive method that scans a form for controls that im interested in - text, combo, dtpicker etc, and it fills an arraylist of them. i then use this arraylist to put the majority of controls into the enableInAddMode for example.. but the primary key box that is auto generated.. that has to be moved into the disable list. i have methods that manage the lists and remove from the enable when adding to disable etc

So, why not use Me.Controls? cause its a pain in the bum and doesnt hold all the controls.

Why hold the whole control? I dont.. thats what ByRef is for.. im just holding a reference to the visible control :)
 
Did you try changing that particular parameter to ByVal, add an object reference to the collection, retrieve that instance again and invoke some visual property to see if it works anyway?
 
Ok, here's a loop that will iterate all the controls on a form as well as all container controls. (Aknowledgements go to JmCilhinney for this one.)

VB.NET:
Dim ctl As Control = Me.GetNextControl(Me, True) 'Get the first control.
        Dim ctlCount As Integer = 0

        While Not ctl Is Nothing
            ctlCount += 1

            If TypeOf ctl Is TextBox Then
                CType(ctl, TextBox).enabled = True
            End If

            ctl = Me.GetNextControl(ctl, True)
        End While

Havent had time to absorb your whole post, i'll probably need to read it a couple of times. Havent heard about the whole byval/byref thing. I have to say that, that is interesting? i'll need to research that one before i could comment. I understand why you would want to pass everything byref, moving memory is faster, but in all the documentation i've ever read, in vb.net byref is used rarely. I'll get back to you shortly, it's just that i'm starving and need some lunch.:)
 
ok.. i solved this one by enhancing my knowledge of vb.. i went back and looked a whole lot at ByRef and ByVal and as i mentione d afew times, ByVal doesnt actually mean that the object gets passed by value, as (just about everyone) might reasonably think it does...

The only things to ever be passed by value are things that inherit from System.ValueType. Anything that doesnt inherit for this is by default, a reference type

When you pass something ByVal, what gets passed is the heap address of the object being passed. If you have an object living at 0x1000 in memory, and you pass it ByVal, all that gets passed is 0x1000

When you pass something ByRef, what gets passed is the stack address of the pointer to the object in memory. Suppose the stack address for our object at 0x1000, was 0x2D. Passing the object ByRef passes the 0x2D, which technically allows us to re-assign our reference to a different object entirely..

Suppose we have this:

VB.NET:
Sub MySubRef(ByRef s as String)
  s = "goodbye world"
End Sub
Sub MySubVal(ByVal s as String)
  s = "goodbye world"
End Sub

and we say:
VB.NET:
Dim test as String = "hello world"
MySubVal(test)
'test still says hello world - in the sub a new string was created and the 
'pointer created by the [b]s as String[/b] was pointed to the new object
'when then went out of scope and was lost
 
MySubRef(test)
'test now says "goodbye world" - in the sub a new string was created
'and because [b]byref s as string[/b] established with us a way to change
'which string was pointed to, test was re-pointed to a new string elsewhere
'in memory.


so, the stupidity was in my understanding of byref and byval - i made a guess that i suppose a lot of people would make and it wasnt right..

mmmmm.. pointers.. mmmm lovely! :)
 
the essential way to remember it is:

ByRef and ByVal refer to how the POINTER to the object is passed,not how the object is passed..

If you pass the pointer by value, you make a copy of the pointer
If you pass the pointer by ref, you pass (a reference to) the original pointer


If you pass a copy of the pointer you can point to whatever you like, the orignl pointer stays pointing to the original object
If you pass a reference to the pointer, you can repoint it to something else.



byref is seldom used, i guess because you then have subs that are capable of taking your calling method's variable, and pointing it to something else - something they dont *really* have the right to do
 
JohnH said:
Did you try changing that particular parameter to ByVal, add an object reference to the collection, retrieve that instance again and invoke some visual property to see if it works anyway?

I did! :)

and, for the reasons explained above, the error goes away because of the way byref and byval do their work.. I didnt actually need to use ByRef because i never intended to repoint the original control to anything - I use ByVal and it works just fine because it works in pass-the-object-by-reference style as i expect..

Hope this thread helps more people clear up the byref/byval issue
 
Back
Top