Hummm ... not a workable design.
My plan was that it the properties for ItemA or ItemB ever changed
someone would just write a new class that inherits the old one and change
the enumeration,
When the new class inherits from the old class (i.e. Item_A_Class or
Item_B_Class class) it will inherit the enumeration Properties also.
However, the new class will not be able to override the enumeration. It
will only be able to shadow the enumeration. This means that you will
have to completely duplicate the enumeration in each derived class. This
completely defeats a crucial purpose of object orientated design; code
re-use.
An enumeration is not an effective type for storing a list of items. The
call to get the element names in the enumeration is very slow. Using the
element names as the data storage is not what an enumeration is designed
for. There are many other types that implement the IList interface that
are more efficient at storing and retrieving items in a list. Use one of
these instead.
The manner in which you want to use one variable type (Dynamic_Class) to
access methods in two different types (Item_A_Class and Item_B_Class) is
known as polymorphism in object oriented design. In order for you to
implement this with your Item_A_Class and Item_B_Class classes you need
something common to both of them that the Dynamic_Class class can use to
access them. Currently there is nothing common in both classes. You may
think that and enumeration named Properties is common to both but consider
this; the system sees the enumerations as Item_A_Class.Properties and
Item_B_Class.Properties, two completely different enumerations.
So, how do we apply polymorphism to these classes? There is a method that
is slow and prone to run-time error. Simply make Dynamic_Class of type
Object. The Object type is common to all types. Then use late-binding to
access the Properties enumeration. For example,
Dim Dynamic_Class As Object = New Item_A_Class()
Dim properties as String() = _
System.Enum.GetNames(GetType( _
Dynamic_Class.Properties))
As I said above this is a very messy approach as there is no design-time
or compile-time type checking. Therefore, it is very easy for you or
another programmer to make errors that can not be caught until the project
is complied and the particular section of code is run. Also, error
checking late bound errors is generally a hellish experience as the
compiler is only able offer limited support.
As you add more item classes with even more enumerations this code will
become slower and even more prone to late-bound errors.
Do not dispair, there is hope. "We can rebuild [it]. We have the
technology." (Six Million Dollar Man, 1970s)
Anyway ... in order to cleanly get what you ultimately are seeking we need
to redevelope your design to take advantage of inheritance so polymorphism
and hence early-binding and strong type-checking with design-time support
can be applied. First we need to decide on a type to hold your property
data. As stated above any one of the types that implement the IList
interface will suffice. Since we do not want consumers of these objects
to modify, delete or add property names we will use an ArrayList. Now we
can define a class ItemBase that has a protected ArrayList and a public
read only Properties property.
Public MustInherit Class ItemBase
Protected _propertyNames As ArrayList
Public ReadOnly Property Properties() As ArrayList
Get
Return ArrayList.ReadOnly(_propertyNames)
End Get
End Property
End Class
We will make the ItemBase type common to all item classes through
inheritance.
Public Class ItemA
Inherits ItemBase
Public Sub New()
Dim names As String() = {"prop_1", "prop_2", "prop_3"}
_propertyNames = New ArrayList(3)
_propertyNames.AddRange(names)
End Sub
End Class
Public Class ItemB
Inherits ItemBase
Public Sub New()
Dim names As String() = {"prop_A", "prop_B", "prop_C"}
_propertyNames = New ArrayList(3)
_propertyNames.AddRange(names)
End Sub
End Class
Now we can use an ItemBase variable to access the common Properties
property of any item class.
Private Sub BuildPropertiesPanel(ByVal Item As String)
Dim ibProperties As ItemBase
Select Case Item
Case "ItemA"
ibProperties = DirectCast(New ItemA(), _
ItemBase)
Case "ItemB"
ibProperties = DirectCast(New ItemB(), _
ItemBase)
Case Else
Throw New ArgumentOutOfRangeException("Item", _
Item, "Unknown Item requested.")
End Select
Dim i As Integer
Dim alProperties As ArrayList = ibProperties.Properties()
For i = 0 To alProperties.Count - 1
'Build property panel
Console.WriteLine(alProperties(i).ToString())
Next
End Sub
The DirectCast is not needed if Option Strict is not activated (i.e.
ibProperties = New ItemA()).
Finally, through inheritance we can extend an item class to hold more
properties for an item.
Public Class ItemAEx
Inherits ItemA
Public Sub New()
MyBase.New()
Dim extraNames As String() = {"prop_4", "prop_5"}
_propertyNames.Capacity += 2
_propertyNames.AddRange(extraNames)
End Sub
End Class
Now change ItemA to ItemAEx in the BuildPropertiesPanel procedure to see
the new entries printed to the console.
Hope this helps.
Robby.
David A. Osborn said:
Basically I have a series of classes that represent items in my program.
These items all have different properties that need to be displayed in a
property window. When the property window geta focus it will check to se
what item is selected and dynamically build the label boxes on itself
based on what the item is. How I've organized this is each item is its
own class. In each class is an enumeration for that item which contains
the properties for that particular item. So the properties panel needs
to decide what class to use based on the string that is passed to it, and
then loop through the enumerations to dynamically build labels on itself?
I'm looking to do something like this. Obiviously this code/psuedo code,
but hopefully it will portray the idea:
Public Class Item_A_Class
Public Enum Properties
prop_1
prop_2
prop_3
End Enum
Some Funtions
End Class
Public Class Item_B_Class
Public Enum Properties
prop_A
prop_B
prop_C
prop_D
End Enum
Some Functions
End Class
Private Sub Build_Properties_Panel (ByVal Item As String)
If Item = "ItemA" then
Dim Dynamic_Class As Item_A_Class
If Item = "ItemB" then
Dim Dynamic_Class As Item_B_Class
EndIf
'Loop through enumeration for class
Dim properties as String() =
System.enum.GetNames(GetType(Dynamic_Class.properties))
For i As Integer = 0 to name.Length -1
do code to build label
Next
End Sub
My plan was that it the properties for ItemA or ItemB ever changed
someone would just write a new class that inherits the old one and change
the enumeration, and the display could would continue to work perfectly
fine. I suppose I could just do the above loop in each if statement
specifically for the class, but I thought the code would be more concise
if I dynamically pointed Dynamic_Class to which ever class it needed to
be and then only had one loop in the subroutine.
Thanks for the help!