Instantiating class variables in parent constructor.

R

RB

Hi guys (and gals!),

I've got 2 classes, "TypesafeConstant" and "Color". "Color" inherits
from "TypesafeConstant", and adds no new functionality. All "Color" does
is to instantiate some class variables which are public instances of
"Color".

What "TypesafeConstant" does is to create a shared list, and add every
declaration of itself to that list. I can then query that list with a
string (e.g. "Red") to return Color.Red.

Now, if I take out the inheritance, and just have "Color" instantiating
itself, everything works fine, but with the inheritance the Parse
function errors with a "NullReferenceException", which is caused by
TypesafeConstant.list being a null reference.

Any ideas how I could re-write this to make it work?

Oh, and just to say, I want to use this technique a lot, hence the
inheritance - I don't want to have to re-write the entire
TypesafeConstant each time!!

Many Thanks,

RB.


************* CODE *******************
Public Class TypesafeConstant
Private sShortType

Protected Shared list As ArrayList

Protected Sub New(ByVal sShortType As String)
Me.sShortType = sShortType
If list Is Nothing Then
list = New ArrayList()
End If
list.Add(Me)
End Sub

Public ReadOnly Property Value() As String
Get
Return sShortType
End Get
End Property

Public Shared Function Parse(ByVal sValue As String) As
TypesafeConstant
Dim o As TypesafeConstant
For Each o In list.ToArray(GetType(TypesafeConstant))
If o.Value.ToUpper = sValue.ToUpper Then
Return o
End If
Next
End Function
End Class


Public Class Color
Inherits TypesafeConstant

Public Shared Red As Color = New Color("Red")
Public Shared GreenAs Color = New Color("Green")
Public Shared BlueAs Color = New Color("Blue")

Private Sub New(ByVal sShort As String, ByVal sLong As String)
MyBase.New(sShort, sLong)
End Sub
End Class
 
R

RB

Terry said:
You might try calling your constructors with the correct number of arguments.

Sorry, my bad. I was trying to simplify the code for the newsgroup.

The code for Color should, of course, read

Private Sub New(ByVal sShort As String)
MyBase.New(sShort)
End Sub

Cheers,

RB.
 
G

Guest

It may be better if you actually show how you hoped to use the color class.
Since the constructor is private, how do you create an instance? Maybe Color
should have a shared sub Initialize with code something like:
If list is nothing then
Red = New Color("Red")
.....etc
end if
 
P

Patrice

The list is initialized in an instance constructor and used in a shared
method. How do you use those classes ? If you try to use the shared method
without creating an instance first it could fail...
 
R

RB

I thought that because the instances were themselves shared, they would
be created before the Parse shared method could be called. It certainly
works that way if I don't use inheritance, and have all the code in a
single class.

I should say that I'm a Java programmer by nature, where this technique
is a recognised pattern, so I might be missing something fundemental
about VB...!

Having said that, I never tried it with inheritance in Java either, so
maybe it's just a stupid thing to do :)

RB.
 
R

RB

Terry said:
It may be better if you actually show how you hoped to use the color class.
Since the constructor is private, how do you create an instance? Maybe Color
should have a shared sub Initialize with code something like:
If list is nothing then
Red = New Color("Red")
.....etc
end if

The instances are created inside the class itself - see the following
lines in class Color:

Public Shared Red As Color = New Color("Red")
Public Shared GreenAs Color = New Color("Green")
Public Shared BlueAs Color = New Color("Blue")


An example of using it would be something like:

dim oColor as Color
oColor = Color.Parse("Red")
if oColor Is Color.Red Then
Messagebox.show ("You chose Red!")
End if

Generally speaking I'm parsing values from a database, but it's along
those lines.

The point of the exercise is to have typesafe constants, similar to the
old Java alternative to enumerators.

Cheers,

RB.
 
R

RB

Terry said:
.....and since the constructor is Private, you can't create an instance!

The TypesafeConstant constructor is protected, which (I think!) means
it's inherited to the child class. The Color constructor is private, but
all instances of it are created inside the class, so that should not be
a problem.

Having said that, I don't really understand why I need a Color
constructor at all. It's just that VB forced me to have one, as it
claimed I needed a no-arg constructor.

The reason it's private by the way, is to limit the set of constants to
those defined in the Color class. Really, Color should be defined as not
inheritable, but that doesn't affect the issue I'm facing.

Cheers,

RB.
 
C

Charlie Brown

The instances are created inside the class itself - see the following
lines in class Color:

Public Shared Red As Color = New Color("Red")
Public Shared GreenAs Color = New Color("Green")
Public Shared BlueAs Color = New Color("Blue")

An example of using it would be something like:

dim oColor as Color
oColor = Color.Parse("Red")
if oColor Is Color.Red Then
Messagebox.show ("You chose Red!")
End if

Generally speaking I'm parsing values from a database, but it's along
those lines.

The point of the exercise is to have typesafe constants, similar to the
old Java alternative to enumerators.

Cheers,

RB.

I may be missing what you are trying to accomplish, but if all you
want is color by name, the System.Drawing.Color class already provides
this function with the Color.FromName method. This would accomplish
your example usage posted above. Then again, you may need something
else I don't see becuase you simplified it for the sake of posting.
 
R

RB

Charlie said:
I may be missing what you are trying to accomplish, but if all you
want is color by name, the System.Drawing.Color class already provides
this function with the Color.FromName method. This would accomplish
your example usage posted above. Then again, you may need something
else I don't see becuase you simplified it for the sake of posting.

Yeah, if it was just colors I would use that method! It's actually
various statuses of various business objects - I just chose colors as an
easier to understand example. Probably Pets would have been a better
choice of an example :)

Cheers,

RB.
 
M

Mindgeek

Give this a try:

Public Class TypesafeConstant
Private sShortType As String

Protected Shared list As ArrayList

Protected Sub New(ByVal sShortType As String)
Me.sShortType = sShortType
If list Is Nothing Then
list = New ArrayList()
End If
list.Add(Me)
End Sub

Public ReadOnly Property Value() As String
Get
Return sShortType
End Get
End Property

Public Shared Function Parse(ByVal sValue As String) As Color
Dim o As Color
For Each o In list
If o.Value.ToUpper = sValue.ToUpper Then
Return o
End If
Next
Return Nothing
End Function
End Class


------------TEST-------------

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim oColor As Color
oColor = Color.Parse("Blue")
If oColor Is Color.Blue Then
MessageBox.Show("You chose blue!")
End If
End Sub
 
P

Patrice

Interesting. AFAIK shared members are guaranteed to be "initialized" the
first time you are using a class. Here it could be that you are not really
using the Color class as the Parse method is shared and actually provided by
the parent class. So technically your code could be just a call to
TypesafeConstant.Parse (and adding a method in the color class and calling
it is enough to get rid of this problem).

I use generally an assumption that is simplest than what really happens and
avoid to introduce side effects regarding initialization order (basically my
assumption is that a shared member is initialized somewhere between the
start and my first use of *this* shared member, the reality is AFAIK that
they are ALL initialized the first time the class is used but it llkks like
to be defeated when using actually what is a static method on the parent
class).

Though this instanciation issue is interesting in itself you may want also
to explain what you are trying to do in case someone would have another
approach...
 
G

Guest

Hi Again,
The problem is that
'dim oClor as Color' does not create an instance of the color class and the
constructor does not get called, so you have no underlying arraylist. One
not so pretty workaround is as follows:
in your TypeSafeConstantClass add:
Protected Sub New()
If list Is Nothing Then
list = New ArrayList()
End If
End Sub

and then in your Color class add:
Public Sub New()
MyBase.New()
End Sub

then finally, the code you use should be:
Dim oColor As New Color 'you need the New to actually create an instance
so the shared list will be created
oColor = CType(Color.Parse("Red"), Color)
If oColor Is Color.Red Then
......
 
R

RB

Terry said:
Hi Again,
The problem is that
'dim oClor as Color' does not create an instance of the color class and the
constructor does not get called, so you have no underlying arraylist. One
not so pretty workaround is as follows:
in your TypeSafeConstantClass add:
Protected Sub New()
If list Is Nothing Then
list = New ArrayList()
End If
End Sub

and then in your Color class add:
Public Sub New()
MyBase.New()
End Sub

then finally, the code you use should be:
Dim oColor As New Color 'you need the New to actually create an instance
so the shared list will be created
oColor = CType(Color.Parse("Red"), Color)
If oColor Is Color.Red Then
......

Hi Terry,

Many thanks for your input, but that's almost exactly what I'm not
trying to do! The point of the class is to be a typesafe constant, so
allowing people to instantiate new constants is a bad thing!

Here's a link to what it would look like in Java:
http://java.sun.com/developer/Books/shiftintojava/page1.html#replaceenums

I'm trying to do broadly the same thing in VB (which works), but I'm
trying to use inheritance, so that I can save having to re-code certain
things for each enumerator I define. I'm also trying to use an array so
that my Parse method is really easy to implement.

Cheers,

Rowland.
 
G

Guest

But they are not instanciating a new constant as the constructor does not
take an argument. Therefore they cannot add any constants to the list. If
you don't like that approach, then add a Shared Sub to Color class that does
not do anything and call it.

like this;

Public Shared Initialize()
End Sub

then your code to use it is

Color.Initialize()
Dim oColor as Color
oColor = Color.Parse.....
 
G

Guest

Have another solution you may like better...

add this to the color class

Public Shared Shadows Function Parse(ByVal sValue As String) As Color
Dim t As TypesafeConstant = TypesafeConstant.Parse(sValue)
Return CType(t, Color)
End Function

Now the code you showed using the color class, will work.
 
R

RB

Terry said:
Have another solution you may like better...

add this to the color class

Public Shared Shadows Function Parse(ByVal sValue As String) As Color
Dim t As TypesafeConstant = TypesafeConstant.Parse(sValue)
Return CType(t, Color)
End Function

Now the code you showed using the color class, will work.

Hi Terry,

That's basically what I did in the end, with one slight change - because
the TypesafeConstruct class is intended to be inherited by a number of
classes, the ArrayList list had to become a list of lists, where the
parent list lists child-class-types, and the sub-list lists the
child-class-members.

One final question before listing the code - what's the easiest way to
get a System.Type from a class name in a shared method. I know I can use

System.Type.GetType("MyNamespace.Pet")

or similar, but I was wondering if there was something that looked more
like:

System.Type.GetType(MyClass.Name)

I can't use Me or MyClass though, as it is a shared method.

Anyhoo my final code looks like:

Public Class TypesafeConstant
Private sShortType

Protected Shared list As Hashtable

Protected Sub New()
If list Is Nothing Then
list = New Hashtable()
End If
End Sub
Protected Sub New(ByVal sShortType As String)
Me.sShortType = sShortType
If list Is Nothing Then
list = New Hashtable()
End If
If Not list.ContainsKey(Me.GetType) Then
list.Add(Me.GetType, New ArrayList())
End If
DirectCast(list.Item(Me.GetType), ArrayList).Add(Me)
End Sub

Public ReadOnly Property Value() As String
Get
Return sShortType
End Get
End Property

Public Shared ReadOnly Property GetList(ByVal type As
System.Type) As Object()
Get
Return DirectCast(list.Item(type), ArrayList).ToArray(type)
End Get
End Property

Public Shared Function Parse(ByVal type As System.Type, ByVal
sValue As String) As TypesafeConstant
Dim o As TypesafeConstant
For Each o In GetList(type)
If o.Value.ToUpper = sValue.ToUpper Then
Return o
End If
Next
End Function
End Class

Public Class Color
Inherits TypesafeConstant

Public Shared Red As Color = New Color("Red")
Public Shared Green As Color = New Color("Green")
Public Shared Blue As Color = New Color("Blue")

Private Sub New()
MyBase.New()
End Sub
Private Sub New(ByVal sShort As String)
MyBase.New(sShort)
End Sub

Public Overloads Shared Function Parse(ByVal sValue As String)
As Color
Return Parse(Red.GetType, sValue)
End Function
End Class


Public Class Pet
Inherits TypesafeConstant

Public Shared Cat As Pet = New Pet("Cat")
Public Shared Dog As Pet = New Pet("Dog")
Public Shared Bird As Pet = New Pet("Bird")

Private Sub New()
MyBase.New()
End Sub
Private Sub New(ByVal sShort As String)
MyBase.New(sShort)
End Sub

Public Overloads Shared Function Parse(ByVal sValue As String)
As Pet
Return Parse(Cat.GetType, sValue)
End Function
End Class


'====================== TEST ====================='
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button2.Click

Dim c As Color = Color.Parse("Red")

MessageBox.Show(c.Value)
Dim p As Pet = Pet.Parse("Cat")

MessageBox.Show(p.Value)

End Sub


Thanks for all your help everyone,

Cheers,

RB.
 
P

Phill W.

RB said:
Now, if I take out the inheritance, and just have "Color" instantiating
itself, everything works fine, but with the inheritance the Parse
function errors with a "NullReferenceException", which is caused by
TypesafeConstant.list being a null reference.

Any ideas how I could re-write this to make it work?
Public Class TypesafeConstant .. . .
Protected Shared list As ArrayList

Protected Sub New(ByVal sShortType As String)
Me.sShortType = sShortType
If list Is Nothing Then
list = New ArrayList()
End If
list.Add(Me)
End Sub .. . .
Public Shared Function Parse(ByVal sValue As String) As
TypesafeConstant
For Each o As TypesafeConstant _
In list.ToArray(GetType(TypesafeConstant))
If o.Value.ToUpper = sValue.ToUpper Then
Return o
End If
Next
End Function
End Class

Don't use a Shared variable /alone/ for the List - lock it up as a
Private variable in the base class and expose it via a Protected, Shared
Property.

Private Shared s_List As ArrayList

Protected Shared ReadOnly Property List() as ArrayList
Get
If ( s_List Is Nothing ) Then
s_List = New ArrayList
End If

Return s_List
End Get
End Property

Now, whenever any of your methods go anywhere near the "list", it is
guaranteed to get a valid instance back.

(Either that or use a "static constructor" mechanism that /always/
creates the list, as in

Public Class TypesafeConstant

Private Shared s_List As New ArrayList

HTH,
Phill W.


wrap it in a
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top