Question Collection Adds to All Collection Instance...

isaac.livingston

New member
Joined
Dec 2, 2008
Messages
3
Programming Experience
5-10
Good Day... I hope this finds you well...

I am very much at a loss in trying to resolve a bug that I have in my code, which is quite extensive; here is the issue in a nut shell...

I have created several collections for a Database front end application I am developing, and I have nested them. The primary Class is my Document Class, wich has a collection of Item Classes in it.

The problem that I am having is when I run two or more instances of the class, the items that are added to the first class are also being added to the second. I start each proceedure by decalring a new class instance of Document, which creates a new Collection Instance of my Items Collections.

I don't understand why each class seems to be sharing the Item Collection, even though they are declared independantly from one another, and instanced with the New keyword...

If anyone can help me shed some light on this, I would appreciate it.
 
It is possible to share some sample code? As little code as possible to repro the issue is sufficient.
 
I don't understand why each class seems to be sharing the Item Collection, even though they are declared independantly from one another, and instanced with the New keyword...

Did you declare either the class to be shared, ot the Item colelction to be shared, or did you do it in a module? Remember that complex types are references, so if you do:
VB.NET:
Dim d1 as New Document
Dim d2 as New Document
d1.Items = New Item(10)
d2.Items = New Item(10)

For i = 1 to 10
  Dim tmpItm as New Item
  d1.Items(i) = tmpItm
  d2.Items(i) = tmpItm
Next i

[B]d1[/B].Items(0).ItemText = "hello world" 

MessageBox.Show([B]d2[/B].Items(0).ItemText) 'hello world!

We need to see code.. youre either using a shared collection, or youre putting the same item in both thereby establishing 2 references to one object
 
Sample Code...

Primary Document Creation:
VB.NET:
    Public docObject As cls_Document = New cls_Document
    Private dbFun As DatabaseOperations
    Private lConn As cls_DataConnectionObjects

    Public Sub New()

        ' This call is required by the Windows Form Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        docObject.DocumentID = Format(Date.Today.Year, "0000") & Format(Date.Today.Month.ToString, "00") & Format(Date.Today.Day, "00") & Format(GetNextDocumentID(), "00000")

    End Sub

This is a sample in how I add Items to the document...

VB.NET:
            Dim lItem As cls_ItemObject = New cls_ItemObject()
            lItem = New cls_ItemObject
            lItem.ItemID = Me.lstSearchResult.SelectedItems(0).Text
            lItem.ItemDescription = Me.lstSearchResult.SelectedItems(0).SubItems(1).Text
            srcForm.docObject.ItemCollection.Add(lItem)

            Dim xFrm As New frm_ItemDetail(srcForm.docObject.ItemCollection.Item(srcForm.docObject.ItemCollection.Count - 1))

            lItem = Nothing
            Me.Close()

Thanks for your help so far, I really appreciate it...
 

Attachments

  • cls_document.txt
    5.7 KB · Views: 10
  • cls_item.txt
    21.3 KB · Views: 7
I don't understand why each class seems to be sharing the Item Collection,
You have declared it Shared, and you don't understand why it appears to be shared? Very funny.

Try these classes:
VB.NET:
Class Document
    Public Shared docItems As New List(Of Item)
End Class

Class Item
    Public Id As Integer
End Class
And this sample code:
VB.NET:
Dim doc1 As New Document
Dim doc2 As New Document
Dim item As New Item
item.Id = 1
doc1.docItems.Add(item)
MsgBox(doc2.docItems(0).Id)
This is the short version of what you posted. It looks like sharing does work. Also notice the warning for doc1.docItems:
Access of shared member, constant member, enum member or nested type through an instance; qualifying expression will not be evaluated.
This means that doc1 in this case is not used, because shared members can only be qualified by the class name, so "doc1.docItems.Add(item)" really is the same as "Document.docItems.Add(item)" - you see? Neither doc1 or doc2 instances mean a thing here.

What you have done is equivalent to this:
VB.NET:
Class Document
    Public Shared docItems As New List(Of Item)

    Public Sub Add(ByVal item As Item)
        docItems.Add(item)
    End Sub
End Class
Then "doc1.Add(item)", which now does not produce the shared/instance warning, because using "docItems" within the Document class is same as qualifying it fully "Document.docItems", but you are still adding to the same shared collection.
 
This is the short version of what you posted. It looks like sharing does work. Also notice the warning for doc1.docItems:

This means that doc1 in this case is not used, because shared members can only be qualified by the class name, so "doc1.docItems.Add(item)" really is the same as "Document.docItems.Add(item)" - you see? Neither doc1 or doc2 instances mean a thing here.

Mmm.. in C# that would have been an error, and the compiler would have forced use of the type name rather than the instance name. How does one deactivate this "allow instance names to refer to static members" rule in VB.NET? Option Strict? Option Explicit? Treat Warnings As Errors? The OP may want to enable it
 
Small point of note: This would be a nicer, more modern way to format your strings:


docObject.DocumentID = string.Format("{0:yyyyMMdd}{1:0000}", DateTime.Now, GetNextDocumentID())


Note GetNextDocumentID() MUST return a numeric data type. If it returns a string, convert it to a number:

string.Format("{0:yyyyMMdd}{1:0000}", DateTime.Now, Convert.ToInt32(GetNextDocumentID()))

For more info why, see: String.Format Method (String, Object) (System)

The formatstring appearing after : can be anything that is valid to pass to .ToString on the type of object youre formatting. Thus the code above is similar to:

docObject.DocumentID = DateTime.Now.ToString("yyyyMMdd") & GetNextDocumentID().ToString("0000")


Try to use .ToString(format) or string.Format(), not Format().. Your code is more readable as:

string.Format("Name: {1}{0}Age: {2}{0}DOB: {3:dd-MMM-yyyy}{0}", Environment.NewLine, person.Name, person.Age, person.Birthdate)

than:

"Name: " & person.Name & Environment.NewLine & "Age: " & person.Age & Environment.NewLine & "DOB: " & person.Birthdate.ToString("dd-MMM-yyyy") & Environment.NewLine
 
Last edited:
Mmm.. in C# that would have been an error, and the compiler would have forced use of the type name rather than the instance name. How does one deactivate this "allow instance names to refer to static members" rule in VB.NET? Option Strict? Option Explicit? Treat Warnings As Errors? The OP may want to enable it
Option Strict allows it, but there is a setting "Instance variable accesses shared member" that can be changed from "warning" to "error", also "Treat all warnings as error" can be enabled. These setting is in project setting, Compile tab.
 
Working Through It....

Thanks for the tip on the String Formatting... I appreciate it!

Secondly, if I remove the Shared declaration, then I generate errors; that specify: "Reference to a non-shared member requires an object reference."

Is there any way to work around this? I really don't want to share my Items... The best way that I can explain what I'm trying to do is

Documents = Books
Items = Chapters

And I don't want duplicate chapters between books. I must be missing something rather basic here... I just don't see it.
 
This thread may give you some help on custom collections: http://www.vbdotnetforums.com/vb-ne...30284-custom-object-display-modification.html
Use Shared keyword if you want something to be shared, else don't, that's a good start for the class design.
In your post the structItems structure (which should be a class) is supposed to be the collection class, not refer to it by external instance.
Did you see the first Document and Item classes I posted? You can remove the Shared keyword there and it suddenly behaves as you want.
 
Back
Top