|
-
Mar 17th, 2022, 01:35 PM
#1
Thread Starter
New Member
Collections and Items
Hi Everyone. I've got a collection class clsColUserAccounts that holds all the Items in a Private List(Of clsUserAccount). I need a way to enact a change/edit of a User's Account Object Item, say the User's Name. Something like objUserClass.Change("David", "Address", "Phone", "Email").
My issue is this: I want any changes to an Object Item to go through the Collections Class, something like objColUserAccounts.Change(IndexOfUserAccountToChange, "David", "Address", "Phone", "Email"). I don't want it to be possible, programatically, to be able to change the details outside of the Collection Class of an Item in the Collection.
The reason why is that the collection needs to "know" if something has changed so that the Collection class can serialize the List(Of) and save the collection to an Application Setting string.
Hope this makes sense. Thanks for reading - William
-
Mar 17th, 2022, 03:52 PM
#2
Re: Collections and Items
So never give them access to it. If the list is private, only the containing class can change it.
-tg
-
Mar 17th, 2022, 03:56 PM
#3
Re: Collections and Items
Depending on how many items the clsUserAccount has, I would suggest having the collection class include a bunch of methods to change each item. This could be done in a variety of ways. If there are only a handful of items, then you might have methods like: ChangeName, ChangeAddress, etc., each of which takes an index and the new value as arguments.
If there are lots of different fields that could be changed, then that might be either tedious, or somewhat overwhelming (since you'd end up with loads of methods to search through for the one you wanted). In that case, you might just have a Change method that took the field name as one argument, the index as a second argument, and the new value as a third argument.
Another approach would be to change clsUserAccount around a bit. For example, you could have that raise custom events. If all the fields on the class were exposed via properties, then you could add some code to the Set block of the property that checks whether the new value is different from the old value, and raise the event if it is. In that case, the collection could simply handle any event from any of the classes in the collection. In other words, the items in the collection would be telling the collection that a change happened. This could be a bit tedious if the number of items in the collection was changing rapidly, as you'd have to be hooking and unhooking event handlers. Get that right, though, and it would work pretty smoothly.
My usual boring signature: Nothing
 
-
Mar 17th, 2022, 04:11 PM
#4
Re: Collections and Items
 Originally Posted by Shaggy Hiker
Depending on how many items the clsUserAccount has, I would suggest having the collection class include a bunch of methods to change each item. This could be done in a variety of ways. If there are only a handful of items, then you might have methods like: ChangeName, ChangeAddress, etc., each of which takes an index and the new value as arguments.
If there are lots of different fields that could be changed, then that might be either tedious, or somewhat overwhelming (since you'd end up with loads of methods to search through for the one you wanted). In that case, you might just have a Change method that took the field name as one argument, the index as a second argument, and the new value as a third argument.
Another approach would be to change clsUserAccount around a bit. For example, you could have that raise custom events. If all the fields on the class were exposed via properties, then you could add some code to the Set block of the property that checks whether the new value is different from the old value, and raise the event if it is. In that case, the collection could simply handle any event from any of the classes in the collection. In other words, the items in the collection would be telling the collection that a change happened. This could be a bit tedious if the number of items in the collection was changing rapidly, as you'd have to be hooking and unhooking event handlers. Get that right, though, and it would work pretty smoothly.
You could inherit the default collection class, override the add, delete, insert, etc. methods and wire up the events in the overrides.
Last edited by PlausiblyDamp; Mar 17th, 2022 at 05:29 PM.
-
Mar 17th, 2022, 04:13 PM
#5
Thread Starter
New Member
Re: Collections and Items
 Originally Posted by techgnome
So never give them access to it. If the list is private, only the containing class can change it.
-tg
the list is private but exposes clsColUserAccounts.Items(n) this is the problem.
-
Mar 17th, 2022, 04:14 PM
#6
Thread Starter
New Member
Re: Collections and Items
Can't use inherit because I'm using <Serialize> and it confuses the serializer.
-
Mar 17th, 2022, 04:15 PM
#7
Thread Starter
New Member
Re: Collections and Items
 Originally Posted by PlausiblyDamp
You could inherit the default collection class, override the add, delete, insert, etc. Methods and wire up the events in the overrides.
Surely there is a way to make a method in the Items only accessible to the collection?
-
Mar 17th, 2022, 06:58 PM
#8
Re: Collections and Items
 Originally Posted by William.
Surely there is a way to make a method in the Items only accessible to the collection?
The only way to do that would be to declare both the item and collection classes in their own project/assembly and then declare that method Friend. Friend means that it is accessible only within the same assembly, so the collection class could access it as though it were Public but it would appear as though Private to anyone else. That's exactly how a DataTable is able to create a DataRow by invoking its constructor but you are unable to do so and have to call NewRow on the DataTable, i.e. the DataRow constructor is declared Friend so is only accessible to types in the System.Data.dll assembly.
-
Mar 19th, 2022, 05:42 AM
#9
Re: Collections and Items
Sounds like you want something like this:-
Code:
Imports System.Collections.ObjectModel
Public Interface IUserAccount
ReadOnly Property UserName As String
ReadOnly Property Address As String
ReadOnly Property Age As Integer
End Interface
Public Class UserAccounts
Private _lstUsers As List(Of IUserAccount)
Private _roCollection As ReadOnlyCollection(Of IUserAccount)
Public Sub New()
_lstUsers = New List(Of IUserAccount)
_roCollection = New ReadOnlyCollection(Of IUserAccount)(_lstUsers)
End Sub
Public Sub AddUser(ByVal userName As String, ByVal address As String, ByVal age As Integer)
_lstUsers.Add(New UserAccount(userName, address, age))
End Sub
Public Sub EditUser(ByVal userName As String, ByVal newAddress As String, ByVal newAge As Integer)
Dim user As UserAccount = _lstUsers.FirstOrDefault(Function(acc) acc.UserName.Equals(userName, StringComparison.CurrentCultureIgnoreCase))
If user IsNot Nothing Then
user.Address = newAddress
user.Age = newAge
Else
Throw New Exception($"Cannot find user '{userName}'")
End If
End Sub
Public ReadOnly Property Accounts As ReadOnlyCollection(Of IUserAccount)
Get
Return _roCollection
End Get
End Property
Private Class UserAccount
Implements IUserAccount
Public Sub New(ByVal userName As String, ByVal addr As String, ByVal age As Integer)
Me.UserName = userName
Me.Address = addr
Me.Age = age
End Sub
Public Property UserName As String Implements IUserAccount.UserName
Public Property Address As String Implements IUserAccount.Address
Public Property Age As Integer Implements IUserAccount.Age
End Class
End Class
Here is an example of it's usage:-
Code:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim accounts As New UserAccounts
accounts.AddUser("James", "123 Olive Drive, Blue valley", 23)
accounts.AddUser("Hollis", "981 Graves Alley, Blue valley", 90)
accounts.AddUser("Mary", "14 Danger Road, Crab Bay", 31)
For Each user In accounts.Accounts
Debug.WriteLine($"{user.UserName}, age {user.Age.ToString} lives at {user.Address}")
Next
accounts.EditUser("hollis", "334 Kelly Avenue, Cresent Plains", 92)
Debug.WriteLine("")
For Each user In accounts.Accounts
Debug.WriteLine($"{user.UserName}, age {user.Age.ToString} lives at {user.Address}")
Next
End Sub
End Class
Output:-
Code:
James, age 23 lives at 123 Olive Drive, Blue valley
Hollis, age 90 lives at 981 Graves Alley, Blue valley
Mary, age 31 lives at 14 Danger Road, Crab Bay
James, age 23 lives at 123 Olive Drive, Blue valley
Hollis, age 92 lives at 334 Kelly Avenue, Cresent Plains
Mary, age 31 lives at 14 Danger Road, Crab Bay
The idea behind this design is to use a private class that implements a public interface. Because the class itself is private within the collection class, only the collection class can actually change the object's data. The interface is exposed publicly but changes cannot be made to the object through that interface. The interface is read-only.
Last edited by Niya; Mar 19th, 2022 at 06:48 AM.
Reason: Improvement suggested by jmcilhinney
-
Mar 19th, 2022, 05:51 AM
#10
Re: Collections and Items
 Originally Posted by Niya
The idea behind this design is to use a private class that implements a public interface. Because the class itself is private within the collection class, only the collection class can actually change the object's data. The interface is exposed publicly but changes cannot be made to the object through that interface. The interface is read-only.
You don't even need the two sets of properties. You can do away with the ReadOnly properties in UserAccount and just have the full properties implement the interface members. They'll then be read/write when accessed via the class and read-only when access via the interface.
-
Mar 19th, 2022, 06:41 AM
#11
Re: Collections and Items
 Originally Posted by jmcilhinney
You don't even need the two sets of properties. You can do away with the ReadOnly properties in UserAccount and just have the full properties implement the interface members. They'll then be read/write when accessed via the class and read-only when access via the interface.
Oh yes, you're quite correct. Don't know how I managed to overlook that. I corrected it in the post.
-
Mar 19th, 2022, 08:00 AM
#12
Thread Starter
New Member
Re: Collections and Items
 Originally Posted by Niya
Oh yes, you're quite correct. Don't know how I managed to overlook that. I corrected it in the post.
Ahh.. so a private class in a public class module... I get it. Thank you . This looks like the solution I was aiming for. It will still serialize through right?
-
Mar 19th, 2022, 08:49 AM
#13
Re: Collections and Items
 Originally Posted by William.
It will still serialize through right?
Depends on how you're serializing. There are multiple ways to approach serialization in .Net.
-
Mar 19th, 2022, 10:02 AM
#14
Re: Collections and Items
Use models (classes or structures) to store data, use List(Of T) to store multiple "records". Create data access layer and expose only required functionality (get, search, create, update, delete). Hide sensitive information (e.g. passwords) by using DTOs (data transfer objects). Use interfaces so you can have different implementations for "storage" - in memory lists, database, something else. Learn how to clone objects to "remove" the reference (link) to the original class instance and avoid mutability of referenced objects.
Finally, don't do all above inside Button1_Click or DataGridView events.
If it is hard - start simple, create simple tests (as simple as few lines console app that creates your objects and calls some methods), improve and then add more functionality.
-
Mar 20th, 2022, 01:45 AM
#15
Thread Starter
New Member
Re: Collections and Items
 Originally Posted by Niya
Depends on how you're serializing. There are multiple ways to approach serialization in .Net.
Ok so I tried the suggestions but I want to expose the items class... Im thinking an items change event is the way to go?
-
Mar 20th, 2022, 02:18 AM
#16
Re: Collections and Items
 Originally Posted by William.
Ok so I tried the suggestions but I want to expose the items class... Im thinking an items change event is the way to go?
My example exposes the private class through a public interface. What does that have to do with events?
-
Mar 20th, 2022, 03:08 AM
#17
Thread Starter
New Member
Re: Collections and Items
 Originally Posted by Niya
My example exposes the private class through a public interface. What does that have to do with events?
what part of "I want to expose the items" are you having issues digesting?
-
Mar 20th, 2022, 03:10 AM
#18
Thread Starter
New Member
Re: Collections and Items
Im not here for a FB style passive aggressive ruck. My items class is complex and i want to expose it but i dont want to allow devs access to some of the methods. so I could use a change event to save the collection when an item changes.
Last edited by William.; Mar 20th, 2022 at 03:18 AM.
-
Mar 20th, 2022, 04:10 AM
#19
Re: Collections and Items
 Originally Posted by William.
what part of "I want to expose the items" are you having issues digesting?
It's probably the part where the items are already exposed in the example provided. The fact that they are exposed as the public interface type rather than the private class type doesn't change that. The example provided does what you want to do, i.e. exposes the items without exposing all the members of the item type. You already have what you need but you apparently don't realise it, so maybe stop accusing others of being passive aggressive when you're just being aggressive. Someone has volunteered their time to provide you with a solution to your problem. Are you going to say thank you and use that solution or are you going to berate them because you won't take the time to understand what you've been given?
-
Mar 20th, 2022, 04:47 AM
#20
Re: Collections and Items
 Originally Posted by William.
what part of "I want to expose the items" are you having issues digesting?
Hey hey hey....Chill with that attitude.
Now lets be adults for a minute here if that's ok with you. You said you want to "expose the items class". That's what the interface was about:-
Code:
Public Interface IUserAccount
ReadOnly Property UserName As String
ReadOnly Property Address As String
ReadOnly Property Age As Integer
End Interface
Using that interface, the actual data is can be read from the collection through this property:-
Code:
Public ReadOnly Property Accounts As ReadOnlyCollection(Of IUserAccount)
Get
Return _roCollection
End Get
End Property
The data class itself is exposed via it's public interface which only exposes members of the class that you want to expose. The rest remain hidden. What does any of that have to do with events? You expect me read your mind and know what you mean? You didn't explain yourself properly.
Last edited by Shaggy Hiker; Mar 20th, 2022 at 01:04 PM.
Reason: Calming the waters.
-
Mar 20th, 2022, 05:02 AM
#21
Re: Collections and Items
 Originally Posted by William.
so I could use a change event to save the collection when an item changes.
Using my example, if you want to be notified of changes in the collection itself through an event, you could do something like this:-
Code:
Imports System.Collections.ObjectModel
Public Interface IUserAccount
ReadOnly Property UserName As String
ReadOnly Property Address As String
ReadOnly Property Age As Integer
End Interface
Public Class UserAccounts
Private _lstUsers As List(Of IUserAccount)
Private _roCollection As ReadOnlyCollection(Of IUserAccount)
Public Event UserAccountDataChanged As EventHandler(Of UserAccountDataChangedEventArgs)
Public Sub New()
_lstUsers = New List(Of IUserAccount)
_roCollection = New ReadOnlyCollection(Of IUserAccount)(_lstUsers)
End Sub
Public Sub AddUser(ByVal userName As String, ByVal address As String, ByVal age As Integer)
_lstUsers.Add(New UserAccount(userName, address, age))
End Sub
Public Sub EditUser(ByVal userName As String, ByVal newAddress As String, ByVal newAge As Integer)
Dim user As UserAccount = _lstUsers.FirstOrDefault(Function(acc) acc.UserName.Equals(userName, StringComparison.CurrentCultureIgnoreCase))
If user IsNot Nothing Then
user.Address = newAddress
user.Age = newAge
RaiseEvent UserAccountDataChanged(Me, New UserAccountDataChangedEventArgs(user))
Else
Throw New Exception($"Cannot find user '{userName}'")
End If
End Sub
Public ReadOnly Property Accounts As ReadOnlyCollection(Of IUserAccount)
Get
Return _roCollection
End Get
End Property
Private Class UserAccount
Implements IUserAccount
Public Sub New(ByVal userName As String, ByVal addr As String, ByVal age As Integer)
Me.UserName = userName
Me.Address = addr
Me.Age = age
End Sub
Public Property UserName As String Implements IUserAccount.UserName
Public Property Address As String Implements IUserAccount.Address
Public Property Age As Integer Implements IUserAccount.Age
End Class
End Class
Public Class UserAccountDataChangedEventArgs
Inherits EventArgs
Public Sub New(ByVal acc As IUserAccount)
Me.Account = acc
End Sub
Public ReadOnly Property Account As IUserAccount
End Class
Highlighted in red are the changes I made to add this event.
Here is the altered test code:-
Code:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim accounts As New UserAccounts
AddHandler accounts.UserAccountDataChanged, AddressOf AccountDataChanged
accounts.AddUser("James", "123 Olive Drive, Blue valley", 23)
accounts.AddUser("Hollis", "981 Graves Alley, Blue valley", 90)
accounts.AddUser("Mary", "14 Danger Road, Crab Bay", 31)
For Each user In accounts.Accounts
Debug.WriteLine($"{user.UserName}, age {user.Age.ToString} lives at {user.Address}")
Next
accounts.EditUser("hollis", "334 Kelly Avenue, Cresent Plains", 92)
Debug.WriteLine("")
For Each user In accounts.Accounts
Debug.WriteLine($"{user.UserName}, age {user.Age.ToString} lives at {user.Address}")
Next
End Sub
Private Sub AccountDataChanged(ByVal sender As Object, ByVal e As UserAccountDataChangedEventArgs)
Debug.WriteLine($"User data for user '{e.Account.UserName}' has changed.")
End Sub
End Class
-
Mar 20th, 2022, 12:43 PM
#22
Thread Starter
New Member
Re: Collections and Items
Last edited by Shaggy Hiker; Mar 20th, 2022 at 01:05 PM.
Reason: Clarified intent.
-
Mar 20th, 2022, 01:05 PM
#23
Re: Collections and Items
-
Mar 20th, 2022, 01:07 PM
#24
Re: Collections and Items
This isn't FB. You may have learned to go around with your guard up because of all the unmoderated content on the web. It won't last here. If you want to leave, that's your choice. This isn't the Hotel California, after all, but we will remove personal attacks.
My usual boring signature: Nothing
 
-
Mar 20th, 2022, 01:16 PM
#25
Re: Collections and Items
The value of interfaces is not often recognized early on. They can serve several different purposes, not least of which is altering what is exposed and what is not, though that is only one of several advantages to them. Over the years, I've come around to the conclusion that you should rarely create a class without an interface simply because you end up finding so many uses for them. It appears that MS agrees with that assessment, considering that you can now 'extract an interface' from any existing class with just a click or two of the mouse. That makes it sufficiently easy that you could almost create the interface even if you don't see any immediate need. I say almost, only because it does have one negative impact: It adds a bit more clutter to a project. That has no impact on anything other than organization, but it's still an impact.
My usual boring signature: Nothing
 
-
Mar 20th, 2022, 01:28 PM
#26
Re: Collections and Items
 Originally Posted by Shaggy Hiker
It appears that MS agrees with that assessment...
It really says a lot when MS gave COM's object-based system interfaces but a not staple object oriented feature like inheritance. It shows you just how strongly MS believes in the power of interfaces.
-
Mar 20th, 2022, 02:04 PM
#27
Re: Collections and Items
Maybe not, back then. That was the mid-90s. The thinking about inheritance appeared somewhat different back then. For one thing, there was so much discussion about multiple-inheritance and all the perils therein. It may simply be that MS didn't think they could include inheritance without opening the door to multiple-inheritance. Now, we realize that interfaces can do what multiple-inheritance did, but without the drawbacks. That may not have been so well understood at the time when the ANSI standardization of C++ was still being fought over.
My usual boring signature: Nothing
 
-
Mar 20th, 2022, 07:31 PM
#28
Re: Collections and Items
That's a very good point. I forgot how much of a ruckus multiple inheritance caused. Even I didn't like it when I first dipped my toes into C++ a lifetime ago.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|