Home All Groups Group Topic Archive Search About

HashTable.Add Modifies HashTable Items?

Author
12 Sep 2006 4:47 PM
CoderHead
I've got a Web app wherein I store a number of generic collections
(Generic.List) in the application-scope as a HashTable (a make-shift
cache), indexed by an Enum value - i.e.,
CType(Application.Item("SysTables"),
HashTable).Add(DataAttribute.Database.Recruiting, TableList).  The Enum
values are as follows:

    Enum DataAttribute.Database
        Data = 0
        DataAccess = 1
        EventLog = 2
        Recruiting = 3
    End Enum

I load each of the collections using a method that I call with each of
the four Enum values.  Each collection contains a List of a class
called SystemTable that, among other things, contains a property called
"Database" - a DataAttribute.Database Enum value, like so:

    Public Property Database() As DataAttribute.Database
        Get
            Return InDatabase
        End Get
        Set(ByVal Value As DataAttribute.Database)
            InDatabase = Value
        End Set
    End Property

As I load each collection into the application-scope HashTable, the
previously-added HashTable items are being modified.  For instance,
let's say that the first collection I add is
CType(Application.Item("SysTables"),
HashTable).Add(DataAttribute.Database.Data, TableList), where each
SystemTable in TableList has a "Database" property value of "Data."
When I add the second item (CType(Application.Item("SysTables"),
HashTable).Add(DataAttribute.Database.DataAccess, TableList)), I find
that each one of my SystemTable instances in the first item (Data) now
have a "Database" property value of "DataAccess."

How in the world is the .Add method on HashTable modifying a single
property value of each of the members of an item contained in the
collection?  Why isn't it changing the "ID" property or the "Name"
property?  Since when does the .Add property modify existing members
anyway?

Author
12 Sep 2006 5:15 PM
CoderHead
This test code works, and it's nearly identical to the code I'm running
in my Web app (aside from the obvious complexity of loading a class
from a database, etc.):

Module Module1
    Private _dictionary As New Dictionary(Of Class1.DatabaseEnum, List(Of
Class1))

    Sub Main()
        _dictionary.Add(Class1.DatabaseEnum.Data,
GetCollection(Class1.DatabaseEnum.Data))
        _dictionary.Add(Class1.DatabaseEnum.DataAccess,
GetCollection(Class1.DatabaseEnum.DataAccess))
        _dictionary.Add(Class1.DatabaseEnum.EventHistory,
GetCollection(Class1.DatabaseEnum.EventHistory))
        _dictionary.Add(Class1.DatabaseEnum.Recruiting,
GetCollection(Class1.DatabaseEnum.Recruiting))
    End Sub

    Private Function GetCollection(ByVal InDatabase As
Class1.DatabaseEnum) As List(Of Class1)
        Dim ClassList As New List(Of Class1)
        For count As Integer = 0 To 9
            ClassList.Add(New Class1(InDatabase))
        Next
        Return ClassList
    End Function
End Module
Author
14 Sep 2006 4:23 PM
CoderHead
I don't know if anybody cares at this point or not, but here's the
solution to the Enum problem (note that it's an Enum problem and not a
HashTable or Dictionary problem).

Enums cannot be used as Shared members of a base class. There's no
documentation to support this, but here's what I found: when I use an
Enum as a Shared member of a base class, the last value populated in
the base class is the value returned from all subclasses. That means
whenever I instantiate a new subclass and I give it a new value for the
Enum member, all previously instantiated subclasses now return the new
value when accessed. First of all, WTF? Second, what is the thinking
behind this? My Shared String and Integer members don't change their
values when a new subclass is instantiated. An Enum is a structure and
not a type, but why would that mean that an instance of an Enum can
only hold a single value across the entire application? Weird.
Author
14 Sep 2006 5:38 PM
Brian Gideon
CoderHead wrote:
Show quote
> I don't know if anybody cares at this point or not, but here's the
> solution to the Enum problem (note that it's an Enum problem and not a
> HashTable or Dictionary problem).
>
> Enums cannot be used as Shared members of a base class. There's no
> documentation to support this, but here's what I found: when I use an
> Enum as a Shared member of a base class, the last value populated in
> the base class is the value returned from all subclasses. That means
> whenever I instantiate a new subclass and I give it a new value for the
> Enum member, all previously instantiated subclasses now return the new
> value when accessed. First of all, WTF? Second, what is the thinking
> behind this? My Shared String and Integer members don't change their
> values when a new subclass is instantiated. An Enum is a structure and
> not a type, but why would that mean that an instance of an Enum can
> only hold a single value across the entire application? Weird.

CoderHead,

That's the behavior I would expect from a Shared (static) class member.
If you want each instance of the class to have its own value then
don't modify the variable using the 'Shared' keyword.  Also, can you
post a short, but complete program demonstrating that shared (static)
string and integer members behave differently than enums?

Brian
Author
14 Sep 2006 7:06 PM
CoderHead
I learned something today. A Shared member in a base class is shared
only to identical instances of the derived class. So if I have Class1
and Class2 that derive from BaseClass, instantiating Class2 won't
affect the Shared members of Class1. For example:
---------------------------------------------------------
Module Module1
    Sub Main()
        Dim MyClasses As New Collection()
        MyClasses.Add(New Class1(AttributeClass.Test.One, "First Try", 100))
        Console.WriteLine("Before MyClasses(1), MyClasses(0) = " &
MyClasses.Item(1).ToString())
        MyClasses.Add(New Class2(AttributeClass.Test.Two, "Second Try", 200))
        Console.WriteLine("MyClasses(0) = " & MyClasses.Item(1).ToString())
        Console.WriteLine("MyClasses(1) = " & MyClasses.Item(2).ToString())
    End Sub
End Module
---------------------------------------------------------
<AttributeUsage(AttributeTargets.Class)> Public Class AttributeClass
    Inherits Attribute
    Enum Test
        One = 1
        Two = 2
    End Enum
    Public EnumVar As Test = Test.One
    Public StringVar As String = String.Empty
    Public IntegerVar As Integer = 0
    Public Sub New(ByVal EnumValue As Test, ByVal StringValue As String,
ByVal IntegerValue As Integer)
        EnumVar = EnumValue
        StringVar = StringValue
        IntegerVar = IntegerValue
    End Sub
End Class
---------------------------------------------------------
Public Class BaseClass(Of t)
    Protected Shared EnumVar As AttributeClass.Test =
AttributeClass.Test.One
    Public Shared StringVar As String = String.Empty
    Public Shared IntegerVar As Integer = 0
    Shared Sub New()
        For Each Attr As Attribute In GetType(t).GetCustomAttributes(True)
            If TypeOf Attr Is AttributeClass Then
                EnumVar = CType(Attr, AttributeClass).EnumVar
                StringVar = CType(Attr, AttributeClass).StringVar
                IntegerVar = CType(Attr, AttributeClass).IntegerVar
            End If
        Next
    End Sub
    Public Sub New()
    End Sub
    Public Sub New(ByVal EnumValue As AttributeClass.Test, ByVal
StringValue As String, ByVal IntegerValue As Integer)
        EnumVar = EnumValue
        StringVar = StringValue
        IntegerVar = IntegerValue
    End Sub
    Public Overrides Function ToString() As String
        Return GetType(t).Name & ", EnumVar=" & EnumVar & ", StringVar='" &
StringVar & "', IntegerVar=" & IntegerVar.ToString()
    End Function
End Class
---------------------------------------------------------
<AttributeClass(AttributeClass.Test.One, "Class1", 999)> Public Class
Class1
    Inherits BaseClass(Of Class1)
    Public Sub New()
        MyBase.New()
    End Sub
    Public Sub New(ByVal EnumValue As AttributeClass.Test, ByVal
StringValue As String, ByVal IntegerValue As Integer)
        MyBase.New(EnumValue, StringValue, IntegerValue)
    End Sub
End Class
---------------------------------------------------------
<AttributeClass(AttributeClass.Test.Two, "Class2", 888)> Public Class
Class2
    Inherits BaseClass(Of Class2)
    Public Sub New()
        MyBase.New()
    End Sub
    Public Sub New(ByVal EnumValue As AttributeClass.Test, ByVal
StringValue As String, ByVal IntegerValue As Integer)
        MyBase.New(EnumValue, StringValue, IntegerValue)
    End Sub
End Class

That explains why my other classes and other properties weren't being
affected. Because the only property I happened to be changing was the
Enum value.
Author
14 Sep 2006 6:51 PM
Jon Skeet [C# MVP]
CoderHead <coderh***@gmail.com> wrote:
Show quote
> I don't know if anybody cares at this point or not, but here's the
> solution to the Enum problem (note that it's an Enum problem and not a
> HashTable or Dictionary problem).
>
> Enums cannot be used as Shared members of a base class. There's no
> documentation to support this, but here's what I found: when I use an
> Enum as a Shared member of a base class, the last value populated in
> the base class is the value returned from all subclasses. That means
> whenever I instantiate a new subclass and I give it a new value for the
> Enum member, all previously instantiated subclasses now return the new
> value when accessed. First of all, WTF? Second, what is the thinking
> behind this? My Shared String and Integer members don't change their
> values when a new subclass is instantiated. An Enum is a structure and
> not a type, but why would that mean that an instance of an Enum can
> only hold a single value across the entire application? Weird.

There's nothing weird about the enum, but I believe you're entirely
mistaken about your String and Integer members.

There's only *one* copy of any particular Shared variable for the
entire AppDomain. Different subclasses don't get extra copies. The
variable is associated with the type it's declared in.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Author
14 Sep 2006 7:12 PM
CoderHead
I am admittedly mistaken about the String and Integer values, but I
believe you're mistaken about derived classes. Run the sample code I
attached a little while ago and you'll see that although Class1 and
Class2 are in the same AppDomain, they receive different copies of the
shared member variables. The proof is that I instantiate Class2 with
different values than Class1 but they both write their disparate values
to the Console. Don't believe me? Check it out. That's why this thing
has been confusing for me.

Jon wrote:
Show quote
> There's nothing weird about the enum, but I believe you're entirely
> mistaken about your String and Integer members.
>
> There's only *one* copy of any particular Shared variable for the
> entire AppDomain. Different subclasses don't get extra copies. The
> variable is associated with the type it's declared in.
> --
> Jon Skeet - <sk***@pobox.com>
Author
14 Sep 2006 7:50 PM
Jon Skeet [C# MVP]
CoderHead <coderh***@gmail.com> wrote:
> I am admittedly mistaken about the String and Integer values, but I
> believe you're mistaken about derived classes. Run the sample code I
> attached a little while ago and you'll see that although Class1 and
> Class2 are in the same AppDomain, they receive different copies of the
> shared member variables.

Well, they have different base types effectively - one is
BaseClass(Of Class1) and the other is BaseClass(Of Class2).

I had indeed neglected to take account of generics, but that's why
you're seeing the different values - it has nothing to do with
subclasses really. Basically, BaseClass(Of Class1) has one set of
static (shared) variables, and BaseClass(Of Class2) has another set.
They're different types. Here's an example (in C#) demonstrating that:

using System;

class Counter
{
    static int next=0;

    public static int Next
    {
        get
        {
            return next++;
        }
    }
}

class Foo<T>
{
    public static int x = Counter.Next;
}

class Test
{
    static void Main()
    {
        Console.WriteLine (Foo<int>.x);
        Console.WriteLine (Foo<string>.x);
        Console.WriteLine (Foo<long>.x);
        Console.WriteLine (Foo<char>.x);
    }
}

Now, if you had a different subclass which still had the same base
class as one of the others, eg:

Public Class Class3    Inherits BaseClass(Of Class1)

then you'd see the shared variables being shared, because they both
genuinely have the same base class.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Author
14 Sep 2006 8:23 PM
CoderHead
Jon Skeet [ C# MVP ] wrote:
Show quote
> CoderHead <coderh***@gmail.com> wrote:
> > I am admittedly mistaken about the String and Integer values, but I
> > believe you're mistaken about derived classes. Run the sample code I
> > attached a little while ago and you'll see that although Class1 and
> > Class2 are in the same AppDomain, they receive different copies of the
> > shared member variables.
>
> Well, they have different base types effectively - one is
> BaseClass(Of Class1) and the other is BaseClass(Of Class2).
>
> I had indeed neglected to take account of generics, but that's why
> you're seeing the different values - it has nothing to do with
> subclasses really. Basically, BaseClass(Of Class1) has one set of
> static (shared) variables, and BaseClass(Of Class2) has another set.
> They're different types. Here's an example (in C#) demonstrating that:
>
> using System;
>
> class Counter
> {
>     static int next=0;
>
>     public static int Next
>     {
>         get
>         {
>             return next++;
>         }
>     }
> }
>
> class Foo<T>
> {
>     public static int x = Counter.Next;
> }
>
> class Test
> {
>     static void Main()
>     {
>         Console.WriteLine (Foo<int>.x);
>         Console.WriteLine (Foo<string>.x);
>         Console.WriteLine (Foo<long>.x);
>         Console.WriteLine (Foo<char>.x);
>     }
> }
>
> Now, if you had a different subclass which still had the same base
> class as one of the others, eg:
>
> Public Class Class3    Inherits BaseClass(Of Class1)
>
> then you'd see the shared variables being shared, because they both
> genuinely have the same base class.
>
> --
> Jon Skeet - <sk***@pobox.com>
> http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
> If replying to the group, please do not mail me too

Thanks for clearing that up, that actually helps me.

AddThis Social Bookmark Button