Custom typed, non-generic, read-only, fixed-sized collection class in VB.Net? - vb.net

I'm looking for suggestions on how to create a custom collection class in VB.Net, to contain instances of a custom object class. There is so much information on the topic that I'm not sure which direction to go in, and custom collections are new to me.
The collection needs are as follows:
collection should be read-only, it and its objects cannot be modified.
collection will always be a fixed size.
because of above two items, add, remove, count, clear, etc aren't needed.
will create & manage all object instances itself at instantiation.
should have a default property like "Item". (?)
should be enumerable, so For-Each can be used on it.
Here is a simplified outline of what I've pieced together so far:
Class Bay
Private ID As Integer
Private p_String As String
Private p_Aisle As Integer
...etc...
...getters, setters, & subs...
End Class
Class Bays
Inherits ...something...
Implements ....somethingelse... (?)
Public ReadOnly MyCollection(5094) as SomeCollectionType (Of Bay)
Private LastUpdate As Date
Private SystemStatus as Integer
Public Sub New()
...instantiate all objects in collection...
End Sub
...properties...
End Class

This is the sort of thing you can do:
Public Class Thing
'...
End Class
Public Class ReadOnlyThingCollection
Inherits ReadOnlyCollection(Of Thing)
Public Sub New()
MyBase.New(GetItems())
End Sub
Private Shared Function GetItems() As IList(Of Thing)
'Generate items as appropriate.
Return {New Thing, New Thing, New Thing}
End Function
End Class

Related

Call baseclass property

It's rather a simple question and both will work. But I'm just wondering what the best practice is. When a child class changes a variable in the baseclass. Should it call the property or just change the underlying variable.
I'm not using the property to do something with the data. The code in the child class is the same. But what is considered the best practice OOP wise?
Sample code:
Public Class TestDirect
Protected temp As Integer
End Class
Public Class TestChldDirect
Inherits TestDirect
Public Sub New()
MyBase.temp = 1
End Sub
End Class
versus
Public Class TestProperty
Private _temp As Integer
'
Public Property temp() As Integer
Get
Return Me._temp
End Get
Set(ByVal value As Integer)
Me._temp = value
End Set
End Property
End Class
Public Class TestChldProperty
Inherits TestProperty
Public Sub New()
MyBase.temp = 1
End Sub
End Class
The second approach gives you more flexibility later on and better protects/hides your underlying implementation. For instance, in your example you might want to modify the type of temp, add some validation etc. Those changes would be more difficult in your first example as you would be affecting the classes that derive from your base class. In the second example you can make the change without affecting any derived classes.

Get a Field Object, not FieldInfo, from a VB Class Instance

I am trying to iterate through objects (fields) in a class and invoke a method on each object. Each object is of a different type. Here is the parent class:
Public Class MySettings
Public IdentifyByFacType As RadioButtonSetting
Public WtrFacTypes As ListSetting
Public OilFacTypes As ListSetting
Public GroupByRef As CheckboxSetting
Public GroupRefAttr As TxtboxSetting
End Class
Here is part of one of the sub-object classes:
<Serializable>
Public Class TxtboxSetting
<XmlIgnore()>
Public MyControl As Windows.Forms.TextBox
<XmlIgnore()>
Public DefaultSetting As String
Private _SavedSetting As String
Public Property SavedSetting As String
Get
Return _SavedSetting
End Get
Set(value As String)
_SavedSetting = value
CurrentValue = value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(DefaultSetting As String, MyControl As Windows.Forms.TextBox)
Me.DefaultSetting = DefaultSetting
Me.MyControl = MyControl
End Sub
Public Sub RestoreDefault()
CurrentValue = DefaultSetting
End Sub
End Class
All of the sub-objects of the MySettings class, like GroupRefAttr for example, have the same methods and properties, but the internal code is different.
So I will have several classes like the MySettings class, and each one will have different sub-objects. Given an instance of such a class, I want to automatically iterate through the fields and call a method RestoreDefault on each one. I don't want to have to know what objects exist in the MySettings class. Rather, knowing that they all have the RestoreDefaultmethod, I want simply call the method on each object.
Despite much searching, I have not found a way to do this. With reflection, I can only get this far:
Dim Opts as New MySettings
For Each var In Opts.GetType.GetFields
Dim RestoreDefault As System.Reflection.MethodInfo = var.FieldType.GetMethod("RestoreDefault")
RestoreDefault.Invoke(Opts, Nothing)
Next
However, in the line RestoreDefault.Invoke(Opts, Nothing), I can't just pass in Opts, as I am dealing with a field in Opts, not Opts itself. A statement like this would work: RestoreDefault.Invoke(Opts.GroupRefAttr, Nothing), but that requires me to know the objects in the MySettings class ahead of time, and that defeats the purpose. Is there a way to grab field instance objects at runtime and pull this off?
When you invoke the RestoreDefault method you need to invoke it on the setting (i.e., the value of the field), not the class containing the setting. Changing your code to this should fix your problem:
Dim Opts as New MySettings
For Each var In Opts.GetType.GetFields
Dim setting As Object = var.GetValue(Opts)
Dim RestoreDefault As System.Reflection.MethodInfo = var.FieldType.GetMethod("RestoreDefault")
RestoreDefault.Invoke(setting, Nothing)
Next
However, if you introduce either a base class or an interface you should be able to get rid of some or all of the reflection. The container setting class can have a collection of settings that each have a shared base class or interface with a RestoreDefault method. The container setting class will then call this method through the base class or interface without having to use reflection.
The base class:
Public MustInherit Class BaseSetting
Public MustOverride Sub RestoreDefault
End Class
A specific settings class:
Public Class TxtboxSetting
Inherits BaseSetting
Public Overrides Sub RestoreDefault()
' Specific implementation
End Sub
End Class
On any class deriving from BaseSetting you can now call the RestoreDefault method without having to use reflection.
However, considering your design you might still want to use reflection to get the settings containd in the MySettings class. You can do it like this:
Dim settings = From fieldInfo in Opts.GetType.GetFields
Where GetType(BaseSetting).IsAssignableFrom(fieldInfo.FieldType)
Select DirectCast(fieldInfo.GetValue(Opts), BaseSetting)
For Each setting In settings
setting.RestoreDefault()
Next
Reflection is used to find all the fields deriving from BaseSetting and then RestoreDefault is called on each field. This method does not suffer from the "magic string" problem where your code depends on the name of the RestoreDefault method represented in a string.
(Also, calling the MySettings class the parent is a bit misleading because there is nothing inheriting from MySettings. Instead this class contains other settings.)
All of the sub-objects of the MySettings class, like GroupRefAttr for example, have the same methods and properties, but the internal code is different.
In that case, the sub-object types should be defined such that they implement a common interface that demands these same methods and properties exist. For now, I'll name that interface IControlSetting. Then, your For loop looks something like this:
Dim Opts as New MySettings
For Each var In Opts.GetType.GetFields
Dim setting As IControlSetting = TryCast(var.GetValue(Opts), IControlSetting)
If setting Is Nothing Then Continue
setting.RestoreDefault()
Next
Additionally, I'd change your MySettings type to encapsulate a dictionary or IControlSetting objects. Then you can just iterate the dictionary to check each of the objects, rather than needing reflection. That might look like this:
Public Class MySettings
Private allSettings As Dictionary(Of String, IControlSetting)
Public Sub New()
allSettings = new Dictionary(Of String, IControlSetting)()
allSettings.Add("IdentifyByFacType", New RadioButtonSetting())
allSettings.Add("WtrFacTypes", New ListSetting())
allSettings.Add("OilFacTypes", New ListSetting())
'...
End Sub
Public Property IdentifyByFacType As RadioButtonSetting
Get
Return DirectCast(allSettings("IdentifyByFacType"), RadioButtonSetting)
End Get
'The setters may be optional, depending on how you expect to use these
Set(ByVal value As RadioButtonSetting)
allSettings("IdentifyByFacType") = value
End Set
End Property
Public Property WtrFacTypes As ListSetting
Get
Return DirectCast(allSettings("WtrFacTypes"), RadioButtonSetting)
End Get
Set(ByVal value As ListSetting)
allSettings("WtrFacTypes") = value
End Set
End Property
Public Property OilFacTypes As ListSetting
Get
Return DirectCast(allSettings("OilFacTypes"), RadioButtonSetting)
End Get
Set(ByVal value As ListSetting)
allSettings("OilFacTypes") = value
End Set
End Property
'...
Public Sub RestoreAllDefaults()
For Each setting As KeyValuePair(Of String, IControlSetting) In allSettings
setting.Value.RestoreDefault()
Next setting
End Sub
End Class

VB Polymorphism Constructors Default and Properties. Similar Class to Listbox

I've been banging my head against a wall for sometime on this one.
I'm trying to create a class for storing data on People with another class to store their Bank Transactions.
Ideally, this all be hidden away and leave only simple statments, declarations and functions available to the programmer. These will include:
Dim Clients As New ClientList
Clients.Count 'readonly integer
Clients.Add("S")
Clients.Refresh()
Clients(n).Remove()
Clients(n).Transaction.Add()
Clients(n).Transaction(n).Remove()
I know this is possible as these exist in the Listbox Class though can't figure out how it's done.
Any help would be appreciated.
Thanks in advance!
Create a Transaction and a Client class
Public Class Transaction
'TODO: Implement Transaction
End Class
Public Class Client
Public ReadOnly Transactions As List(Of Transaction) = New List(Of Transaction)
Public Sub New(ByVal name As String)
Me.Name = name
End Sub
Public Name As String
End Class
Create a ClientList class
Public Class ClientList
Inherits List(Of Client)
Public Overloads Sub Add(ByVal name As String)
Add(New Client(name))
End Sub
Public Sub Refresh()
' Do what ever you want Refresh to do
End Sub
End Class
You can then use the client list like this
Dim clients As New ClientList
clients.Add("S")
' Or
clients.Add(New Client("T"))
Dim n As Integer = clients.Count
Dim m As Integer = clients(0).Transactions.Count
clients.Refresh()
clients.RemoveAt(5)
clients(n - 1).Transactions.Add(New Transaction())
clients(n - 1).Transactions.RemoveAt(2)
Dim name As String = clients(0).Name
Dim client As Client = clients(0)
Use the generic List(Of T) class, specialized to hold your Client objects. It already provides all of the methods you want without your having to write a single line of code!
So you would first write a Client class that contained all of the properties (data) and methods (actions) relating to a "client":
Public Class Client
Public Property Name As String
Public Property AmountOwned As Decimal
Public Sub Bill()
BillingManager.BillClient(Me)
End Sub
' ... etc.
End Class
Then, you would create the List(Of T) to hold all of your instances of the Client class:
Dim clients As New System.Collections.Generic.List(Of Client)
If, for whatever reason, you needed to specialize the behavior of the Add, Remove, etc. methods provided by the collection class, or add additional methods, you would need to change strategies slightly. Instead of using List(Of T), you would inherit from Collection(Of T) and create a custom collection class like so:
Public Class ClientCollection
Inherits System.Collections.ObjectModel.Collection(Of T)
' ... customize as desired ...
End Class
The WinForms ListBox class doesn't do it exactly like this because it was written before generics were introduced to the framework. But since they're here now, and you should always use them when possible, you can completely ignore how WinForms does things.

Caching propertyinfo of abstract class

I've been playing around with implementing an abstract base class that using reflection accomplishes SQL to Object mapping.
I did some benchmarks and decided I wanted to implement a caching strategy for the property info of the objects (to prevent future lookups on them). My first instinct was to try and implement something like this.
Public MustInherit Class BaseModel
Implements IFillable
Private Shared PropertyCache As List(Of PropertyInfo)
Sub New()
PropertyCache = New List(Of PropertyInfo)
For Each itm As PropertyInfo In Me.GetType().GetProperties
PropertyCache.Add(itm)
Next
End Sub
End Class
but then I realized that this would obviously not work because it would get overridden on subsequent object instantiations.
So now Im stuck, how can you implement an abstract class that caches its reflection "metadata"?
EDIT:
This is the best (workaround to my problem) I can come up with, Im hoping someone can suggest something better?
Public MustInherit Class BaseModel
Implements IFillable
Private Shared ReadOnly PropertyCache As New Dictionary(Of String, PropertyInfo)
Sub New()
Dim typeName As String = Me.GetType.ToString
For Each itm As PropertyInfo In Me.GetType().GetProperties
Dim lookupKey As String = String.Format("{0}_{1}", typeName, itm.Name)
If Not PropertyCache.ContainsKey(lookupKey) Then
PropertyCache.Add(lookupKey, itm)
End If
Next
End Sub
End Class
If you place the code in Shared Sub New instead it should only execute once.
http://msdn.microsoft.com/en-us/library/aa711965(v=vs.71).aspx

Method to "override" shared Members in child classes

At the moment I'm trying to create a kind of model in vb.net which can be used to create/fetch database entrys.
I created a main class Model with a shared function to fetch the datasets, e.g. Model.find().
Now I'd like to create Classes which inherit the main Model-Class, e.g. a separate one for users: UserModel.find() => "SELECT * FROM users".
What I need now is to find a way to tell the Class which table it should use. I thought about an abstract String "table" which is a constant in each "child-model", but how could this be implemented as it's not possible to override shared members?
Thanks in advance!
Edit: Maybe this will make it a little clearer what I mean:
Public Class Model
Public Shared _controller As Controller
Public Shared table As String
Protected Shared tableFields As String()
Shared reader As Npgsql.NpgsqlDataReader
Public Shared Function find()
Dim a As ArrayList = New ArrayList
'Test if the tablefields are already known to the class, if not, get them
If tableFields Is Nothing Then
getTableFields()
End If
Dim query As String = "SELECT " + String.Join(", ", tableFields) + " FROM " + table
reader = _controller.executeReader(query)
While reader.Read
o = New Model
Dim v As New Hashtable
For Each field In tableFields
v(field) = reader(field)
Next
o.values = v
a.Add(o)
End While
reader.Close()
Return DirectCast(a.ToArray(GetType(Model)), Model())
End Function
Public values As Hashtable
Public Sub New()
End Sub
End Class
So I want a shared method which finds all database entries and gives back an array of instances of its own type, e.g. Model().
That's why I wanted to keep the find-method shared and not bound to an instance.
I think you could use Generics. Here I´ve pasted an example
All the classes in your domain could inherit from Entity class
Public MustInherit Class Entity
'...
End Class
Your Model class, with your method Find
Public Class Model
Public Shared Sub Find(Of T As Entity)()
' You could know the name of T to find the table
Dim tableName As String = GetType(T).Name
'...
End Sub
End Class
One class of your domain, for example: User class
Public Class User
Inherits Entity
' ...
End Class
And finally, an example of how could you instantiate the Find method
Model.Find(Of User)()
'...
I dunno if this is what you mean, do you find this helpfull?
You could make your main class abstract and each subclass will have to return its "own" table name via its own implementation (e.g. getTableName). This way, you would only have to maintain you method logic in the main class.
It is common to use the Singleton design pattern in such cases: create an instance method, overridden by inheriting classes. Each inheriting class should have that instance method return a Singleton object related to that class.
Here is one way of doing it:
MustInherit Class BaseClass
Public MustOverride Function getTableName() As String
End Class
Class Class1
Inherits BaseClass
Private Shared TableName As String = "myTable1"
Public Overrides Function getTableName() As String
Return TableName
End Function
End Class
Class Class2
Inherits BaseClass
Private Shared TableName As String = "myTable2"
Public Overrides Function getTableName() As String
Return TableName
End Function
End Class
EDIT: a whole different approach.
You can have the base class hold some dictionary, which relates class types (or type names) with the correct table:
Class BaseClass
Private Shared myDictionary As New Collections.Generic.Dictionary(Of Type, String)
Friend Shared Sub RegisterType(ByVal childType As Type, ByVal tableName As String)
myDictionary.Add(childType, tableName)
End Sub
Public Shared Function getTableName(ByVal childType As Type) As String
Return myDictionary.Item(childType)
End Function
End Class
Class Class1
Shared Sub New()
BaseClass.RegisterType(GetType(Class1), "table1")
End Sub
End Class
Class Class2
Shared Sub New()
BaseClass.RegisterType(GetType(Class2), "table2")
End Sub
End Class
Shared (static) objects or object members can't be inherited or overrided. Inheritence is for instance of an object. Since you do not have to instantiate a static class you can't inherit from it. The same with the methods. A static method shouldn't be virtual (Overridable in VB) as it defines a method that perform tasks with no instance of the class. Then this makes it impossible to use instance fields or properties within a static (Shared in VB) method. This is a bad design trying so.
In fact, every static (Shared) classes should be marked as NotInheritable in VB, and defining the default empty constructor only. This is a leak from VB in regards of OOP concepts.