Go Back  Xtreme Visual Basic Talk > Visual Basic .NET (2002/2003/2005/2008, including Express editions) > .NET General > Object and Collection Question


Reply
 
Thread Tools Display Modes
  #1  
Old 02-19-2008, 08:31 PM
zoul1380's Avatar
zoul1380 zoul1380 is offline
Junior Contributor
 
Join Date: Aug 2002
Location: Philippines
Posts: 230
Default Object and Collection Question

i have a collection wherein i store an object for example i have a textbox Object which have a text value = "TEST" then i store this on the collection then i change the text Value to = "SAMPLE" then i notice that the value of the first Text Object the is also change to SAMPLE...

What am doing is an UNDO function wherein i store the object state in a collection... can anybody explain to me why does the value of the textbox object changes in a collection thanks
Reply With Quote
  #2  
Old 02-19-2008, 10:45 PM
Qua's Avatar
Qua Qua is offline
Impetuous & volatile

* Expert *
 
Join Date: Apr 2005
Location: 127.0.0.1
Posts: 2,016
Default

This is most likely because you a changing a property on a object that is stored as a reference to the original object, but try posting your code.
Reply With Quote
  #3  
Old 02-20-2008, 08:57 AM
AtmaWeapon's Avatar
AtmaWeapon AtmaWeapon is online now
Ultimate Contributor

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 7,598
Default

Qua is most likely correct; conveying what is happening is easiest with pictures, but with words it's not so bad either.

There are two types of variables in .NET: reference types and value types. Each behaves differently, and it is imperative that you understand how each is used.

Analogies work great too, so for the purposes of analogy suppose we have a warehouse full of boxes, and we want to be able to talk about a particular box.

Value Types
Value types are the easiest to explain, and work intuitively. All structures are value types, and almost all primitive types (Integer, Double, etc.) are value types. When you declare a value type like this:
Dim value as Integer = 10

...the compiler allocates memory for an Integer variable, stores the value in that memory, and the name value points to the memory location. When you change the value, the value that is stored in the memory location is changed. When you assign one value type to another:
value2 = value

... the compiler copies the value of the right-hand side into the left-hand side. Both variables represent unique locations in memory.

In our warehouse, it's easy to describe how value types work. When I ask for a new integer, I get a box. I can put whatever I want in that box, but I have to remove its old contents before putting it in another box. If I want box 2 to have what's in box 1, I have to find an item identical to what's in box 1 and place it in box 2. If I later come back and change what's in box 2, box 1 remains unchanged.

Reference Types
Reference types are types that might take up a lot of space; every class is a reference type and String is a reference type as well. When you declare and instantiate a new reference type:
Dim value As New SomeClass()

... the compiler allocates memory on the heap for a SomeClass object, and stores the address of that memory in value. When you assign a reference type to another:
value2 = value

... the compiler copies the address of the right-hand side and stores it in the left-hand variable. Now, you have two variables that reference the same object in memory. If you change something via value2, value1 will reflect the change.

In our warehouse, reference-type variables are easy to explain. When I ask for a new reference-type box, someone creates the right box, places it on a shelf, then hands me a piece of paper that tells me where the box is. If I want to change the contents of the box, I find the box and change its contents.

The line of code value2 = value in this case behaves differently; it's as if I found a new piece of paper and wrote the address on that one too. Now, it doesn't matter which piece of paper I use to find the box, there's only one box.

If I don't want this to happen, I have to explicitly tell my workers to find a new box, make sure its contents are identical to the old box, then give me the address of the new box. This is a tricky point for a lot of people.

Summary
  • Value types are primitive types and structures.
  • Value types store a value.
  • When you assign a value type to another, a copy of the first value is made and stored in the other value.
  • Reference types are classes and strings.
  • Reference types store the location of the object they reference.
  • When you assign a reference type to another, only the address is copied.
  • If you don't want to alter the first object, you have to manually make a copy of it and store it in the second object.

Strings are weird
Strings are reference types. Strings are also immutable. What this means is that if I declare a string, then assign that string to a second variable, then change the string via either variable, I still have two unique string objects:

Why? Strings are immutable; this means once a string is created in memory, you cannot alter it. When the string is changed, a new string is returned. So, when you assign two strings to each other, it behaves like a reference and you get two variables that point to the same string. BUT, if I change the value of one of the variables, it points to a new string; since the original variable still points to the old string, strings behave like value types.

The following application demonstrates some of the concepts:
Code:
Module Module1

    Sub Main()
        Console.WriteLine("Value types:")
        Dim intValue As Integer = 10
        Dim intValue2 As Integer = intValue
        Console.WriteLine("Before change: {0} {1}", intValue, intValue2)
        intValue2 = 15
        Console.WriteLine("After change: {0} {1}", intValue, intValue2)

        Console.WriteLine()
        Console.WriteLine("Reference types:")
        Dim clsReference As SomeClass = New SomeClass("one")
        Dim clsReference2 As SomeClass = clsReference
        Console.WriteLine("Same reference? {0}", Object.ReferenceEquals(clsReference, clsReference2))
        Console.WriteLine("Before assignment: {0} {1}", clsReference.value, clsReference2.value)
        clsReference2.value = "two"
        Console.WriteLine("After assignment: {0} {1}", clsReference.value, clsReference2.value)

        Console.WriteLine("Copying...")
        clsReference2 = New SomeClass(clsReference.value)
        Console.WriteLine("Same reference? {0}", Object.ReferenceEquals(clsReference, clsReference2))
        Console.WriteLine("Before assignment: {0} {1}", clsReference.value, clsReference2.value)
        clsReference2.value = "three"
        Console.WriteLine("After assignment: {0} {1}", clsReference.value, clsReference2.value)

        Console.WriteLine()
        Console.WriteLine("Strings:")
        Dim value As String = "one"
        Dim value2 As String = value
        Console.WriteLine("Same reference? {0}", Object.ReferenceEquals(value, value2))
        Console.WriteLine("Before assignment: {0} {1}", value, value2)
        value2 = "two"
        Console.WriteLine("After assignment: {0} {1}", value, value2)
    End Sub

End Module


Public Class SomeClass
    Public value As String

    Public Sub New(ByVal value As String)
        Me.value = value
    End Sub
End Class
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #4  
Old 02-20-2008, 08:04 PM
zoul1380's Avatar
zoul1380 zoul1380 is offline
Junior Contributor
 
Join Date: Aug 2002
Location: Philippines
Posts: 230
Default

here is my code... thanks for the reply....
Code:
    Private UndoQue As New Collection
    Public UndoCount As Integer

    Public Sub AddUndoTask(ByVal uObject As Object)
        UndoQue.Add(uObject)
    End Sub

    Private Sub TextBox1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox1.KeyPress
        Dim obj As New Object

        If e.KeyChar = Chr(13) Then
            obj = sender
            undoObject.AddUndoTask(obj)
        End If
    End Sub
Reply With Quote
  #5  
Old 02-20-2008, 09:02 PM
AtmaWeapon's Avatar
AtmaWeapon AtmaWeapon is online now
Ultimate Contributor

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 7,598
Default

Yep. That's exactly what I described and also a horrible idea. Why not store the value inside of the textbox instead of the entire thing, since there's an OS limit on the number of control handles you can have?
Reply With Quote
  #6  
Old 02-20-2008, 10:08 PM
zoul1380's Avatar
zoul1380 zoul1380 is offline
Junior Contributor
 
Join Date: Aug 2002
Location: Philippines
Posts: 230
Default

ok thanks for the advice
Reply With Quote
  #7  
Old 02-21-2008, 08:05 AM
AtmaWeapon's Avatar
AtmaWeapon AtmaWeapon is online now
Ultimate Contributor

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 7,598
Default

I suppose I could have been more clear; it's better for me to teach you why it's failing then just say "read my post here's how to fix it".

If we step back into my warehouse analogy from my explanation post (you did read it, didn't you?), then it is more clear. TextBox is a reference type, so the variable sender at this point is a slip of paper that tells us how to get to the TextBox. When you do obj = sender, you put the piece of paper on a copy machine, so now you have two pieces of paper with an address on it. When you add obj to the collection, you put the copy into a box.

Now, what happens when the user changes the text box? Since all of your obj papers are the address of the one text box, then the change will affect all variables.

What if you just store the text from the text box? In this case, we're still working with a reference type (see: Strings are Weird), so our variable will still be just an address, and we'll still be putting pieces of paper into our box. However, now when the text of the text box is changed, a new string is created at a new address, and the text box's Text property is updated to report this new address. So, changing the value of the string does not affect your previously stored values.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #8  
Old 06-11-2010, 07:56 AM
CrashPilot CrashPilot is offline
Regular
 
Join Date: Jun 2009
Location: Netherlands
Posts: 61
Default

Another good analogy is the glass of beer approach.

A friend asks you for your glass of beer.

If you would pass that glass of beer by the "ByVal" approach you would first quickly run to the bar and ask the bartender to create a new glass of beer of the same size and brand and fills it with an equal amount of beer. In essence you now have a copy of your original beer and you are probably very happy at this point becouse you now have two beers in your hands.
You then reluctantly give the freshly made beer to your friend and he then imidiatly makes a weird sound and throws it against your newly bought fullHD LED LCD TV destroying the TV and spilling all the beer on the ground. You are ofcourse upset by this act of terrorism and decide that you will drink your sadness away by getting drunk with your original beer.

Now if you would have passed the glass of beer on the "ByRef" way you would just have given him your glass of beer and you would then very sadly have to watch how your friend throws it against the TV thus destroying it and now you cannot get drunk anymore becouse you gave him your only beer!

---

However there is one thing that i do not imideatly understand is: Why is an object that is passed to a function which clearly states the "ByVal" method and then destroy's my TV with it and therefor destroying all it's contents leaving me bedazzled with:
"WTH just happened? I just gave you a new beer and you grabbed my original just out of my hands!" I mean why do i not get a big fat flashing error message and sirene of 180dB from visual studio telling me that passing an object as byval just isn't possible and i should declare byref instead???? (exept strings)

Also i fail to see the difference between a "Pointer" and "ByRef" Both seem to carry a reference to an address in memory. (I mean this as the old ANSI C *p pointers.)
__________________
-- I divided by zero... and survived --
Reply With Quote
  #9  
Old 06-11-2010, 08:21 AM
PlausiblyDamp's Avatar
PlausiblyDamp PlausiblyDamp is online now
Contributor

* Expert *
 
Join Date: Nov 2003
Posts: 602
Default

An object that is a reference type, i.e. a class is always passed as a reference.

To follow Atma's analogy of the warehouse and how a reference type can be described as a slip of paper that tells us how to locate the object itself then passing a reference type ByVal is akin to taking a photocopy of the slip and giving that to whoever needs to locate the object, changes to this slip only affect what this copy points to. If we pass this slip ByRef then we are giving the original slip to the person and if they modify the slip they can alter which object it points to.

Even when passed ByVal however it still points to the same object - changes to the object will be affecting the one and only instance of the object.
__________________
Intellectuals solve problems; geniuses prevent them.
-- Albert Einstein

Posting Guidelines Forum Rules
Reply With Quote
  #10  
Old 06-11-2010, 08:53 AM
CrashPilot CrashPilot is offline
Regular
 
Join Date: Jun 2009
Location: Netherlands
Posts: 61
Default

so in essence byval and byref concludes to the exact same result. The object that is pointed to by the slip (copy or not) will point to the exact same space in memory thus any changes made to it will cary through may that be byref or byval.

In other words byval gives a copy of a pointer and byref gives the original pointer itself. Thusfar i understand that principe and i excpected it to work in this fashion. So in byref i can change the pointer itself to coöincide with my new object i wish to refer( tear up the original slip and write a new one for wallmarts or somthing like that. Sounds to me that byref is no1 way to create memory leaks:

1. Get Pointer original
2. Sneakilly make it point somewhere else
3. Presto original object's reference is lost forever.

So passing a object byref would normally never be used unless you realy want to do something tricky with it?
__________________
-- I divided by zero... and survived --
Reply With Quote
  #11  
Old 06-11-2010, 09:13 AM
PlausiblyDamp's Avatar
PlausiblyDamp PlausiblyDamp is online now
Contributor

* Expert *
 
Join Date: Nov 2003
Posts: 602
Default

You pretty much have it nailed - passing a parameter to a reference type as ByRef will allow you to reassign the original "pointer" to a new object, passing ByVal will not allow this assignment as the method only receives a copy of the pointer and any changes will be local to the method.

Doing things like that isn't going to cause a memory leak under .Net as the garbage collector is responsible for freeing up memory used by objects that are no longer referenced.
__________________
Intellectuals solve problems; geniuses prevent them.
-- Albert Einstein

Posting Guidelines Forum Rules
Reply With Quote
  #12  
Old 06-11-2010, 10:13 AM
AtmaWeapon's Avatar
AtmaWeapon AtmaWeapon is online now
Ultimate Contributor

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 7,598
Default

Quote:
so in essence byval and byref concludes to the exact same result. The object that is pointed to by the slip (copy or not) will point to the exact same space in memory thus any changes made to it will cary through may that be byref or byval.
This is true if and only if the variable is a reference type. For classes, all variables are pointers under the hood. For example:
Code:
Dim someValue As New CustomRef()
This is a reference variable that points at some class CustomRef. In C, you'd write it like this:
Code:
CustomRef * someValue = new CustomRef();
There is no way to create a value-type variable that holds a reference type. That is to say, the following C concept cannot be implemented in .NET:
Code:
CustomRef someValue();
The above would create a value-type variable in C. Since it's not a pointer type, to pass it by reference you'd need to do something special like take the address:
Code:
void DoSomething(CustomRef * someValue) { ... }

CustomRef someValue();
DoSomething(&someValue);
In C, you could start with a reference variable (pointer) and dereference it to get pass by value:
Code:
void DoSomething(CustomRef someValue) { ... }

CustomRef * someValue = new CustomRef();
DoSomething((*someValue));
This is impossible in VB .NET. Classes are reference types and thus the only way to access them is through reference variables. All class variables are pointers.

Quote:
In other words byval gives a copy of a pointer and byref gives the original pointer itself. Thusfar i understand that principe and i excpected it to work in this fashion. So in byref i can change the pointer itself to coöincide with my new object i wish to refer( tear up the original slip and write a new one for wallmarts or somthing like that. Sounds to me that byref is no1 way to create memory leaks:

1. Get Pointer original
2. Sneakilly make it point somewhere else
3. Presto original object's reference is lost forever.

So passing a object byref would normally never be used unless you realy want to do something tricky with it?
Incorrect. As PlausiblyDamp pointed out, .NET is a garbage-collected language. You don't have to explicitly destroy objects. When an object has no references that point to it, the Garbage Collector (GC) will notice this and automatically clean it up. Here's some VB .NET code followed by how the equivalent C++ code would be written (I'm hoping I get this right):
Code:
Public Sub CreateNewReference(ByRef oldReference As CustomRef)
    oldReference = New CustomRef()
End Sub

' Call site:
Dim someReference As New CustomRef()
' ... do some stuff
CreateNewReference(someReference)
'   Now there are no more references to the 1st object, so the next time the GC runs the object
'   will be collected.

--------
// Documentation that explains in excruciating detail that the function will replace the reference
// so the caller knows whether to de-allocate memory or not.
void CreateNewReference(CustomRef * oldReference)
{
    oldReference = new CustomRef();
}

// Call site:
CustomRef * someReference = new CustomRef();
// ... do some stuff
delete someReference;
someReference = 0;
CreateNewReference(someReference);
The C++ code had to do a lot more work because it's important to make it clear who is responsible for freeing the memory. In this case, the function does not take responsibility for freeing the memory, so the call site had to make sure to do so. If the delete line were forgotten in C++ it would cause a memory leak. In .NET, the object will be collected eventually. You could extend the lifetime of the object by storing it in a different variable; this looks similar in both languages:
Code:
Dim someRef As New CustomRef()
Dim oldRef As CustomRef = someRef
CreateNewReference(someReference)
' Now there are 2 objects; oldRef is keeping the first one alive.

--------

CustomRef * someRef = new CustomRef();
CustomRef * oldRef = someRef;
CreateNewReference(someRef);
To me, it's more obvious why the object is still alive in C++, but it's nice to not have to worry about forgetting to free memory in .NET.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #13  
Old 06-14-2010, 07:29 AM
CrashPilot CrashPilot is offline
Regular
 
Join Date: Jun 2009
Location: Netherlands
Posts: 61
Default

True the GC thread cleaning up behind you is a very nice feature. However I am prety sure there are some cases of "unmanaged code" that will break that rule and start to leak memory. But i now fully understand the difference and i can link it with the old C++ pointer type. (old is relative :P).

Ty for that lengthy explanation.
__________________
-- I divided by zero... and survived --
Reply With Quote
  #14  
Old 06-14-2010, 10:29 AM
AtmaWeapon's Avatar
AtmaWeapon AtmaWeapon is online now
Ultimate Contributor

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 7,598
Default

You're wise to believe that it's not a magic bullet. Some types hold on to unmanaged resources; System.Drawing.Bitmap is a good example. Generally, you'll find that these types implement the IDisposable interface and have a Dispose() and/or Close() method. They are written such that when you call Dispose() or Close(), the unmanaged resources are released. They usually also include a special construct called a finalizer. If you forget to dispose of the object, the GC will call its finalizer when it's cleaned up. This isn't as good for your resource usage though; the GC runs when it runs and not at predictable times.

The only other way to cause a memory leak in .NET is to keep a reference to an object that you don't realize you're keeping. It's actually easy to do with events. When you handle an event, the class that raises the event has to keep a reference to the class that handles the event. If the class that raises the event lives longer, it will keep the raiser alive and also leave you with no way to get to the object that can raise the event. This took me a long time to understand, so here's an example.

Suppose you have an application that displays some data from various I/O cards. You have a few user controls for displaying different kinds of data, and there are several different I/O cards the user might choose. The controls handle an event raised by the I/O card interfaces to know when they should updated. To conserve memory, you dynamically load/destroy controls as they are needed.

You might load one of the controls this way:
Code:
Public Sub DisplaySomeControl()
    _currentDisplay = New SomeControl()
    AddHandler _data.DataUpdated, _currentDisplay.DataUpdated
    Me.Controls.Add(_currentDisplay)
End Sub
That AddHandler statement forms what the GC considers a root. A root is a reference that will keep an object alive. The _data object has a root that will keep _currentDisplay alive. Here's how to cause a memory leak:
Code:
Public Sub RemoveCurrentControl()
    Me.Controls.Remove(_currentDisplay)
    _currentDisplay.Dispose()
    _currentDisplay = Nothing
End Sub
Note the event handler was not removed. The user control was disposed, but it doesn't own the root related to the event handler; that's owned by the data interface. Once _currentDisplay is set to Nothing, we have no way to touch the object anymore, but the GC will see the root held by the data interface and refuse to collect it. The only way the user control will ever be collected is if the data interface is collected first.

You can solve the problem by always explicitly removing any event handler you add:
Code:
Public Sub RemoveCurrentControl()
    RemoveHandler _data.DataUpdated, _currentDisplay.DataUpdated
    '...
Note that the problem does not exist in reverse. If the user control handled an event raised by the data interface, then the user control owns the root. When the user control is collected, the root dies with it. In general, you don't have to remove event handlers when the object that raises the event is expected to live for a shorter amount of time than the object that handles the event. This is why when you close a form, you don't have to remove any event handlers for events that its controls raise: when the form dies, the controls die with it, and the GC is smart enough to know that even though they point at each other nothing else points to them.

Rules of thumb:
  • If an object implements IDisposable, always use a Using statement or explicitly call Dispose() or Close().
  • If you call AddHandler, make sure there's a RemoveHandler somewhere.
The 2nd rule is more loose than I imply, because if you know the handler will live longer than the raiser you can get away with it. I find it's easier to just always remove the handler than it is to figure out whether it is required.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Advertisement:

Powered by liquidweb