Search code examples
c#.net.net-micro-framework

What can cause keys added to a Hashtable to be null?


Using the .NET Micro Framework 4.1

I'm adding the following string keys (and string values, not relevant here) to a hashtable:

"eth::address"
"eth::netmask"
"eth::gateway"
"eth::dns"
"eth::port"
"com::baudrate"
"com::parity"
"com::databits"
"com::stopbits"
"com::handshake"
"com::read-timeout"
"com::write-timeout"
"drv::led-firmware-file"
"scr::width"
"scr::height"
"scr::colors"

When adding these to the HashTable no errors are thrown.

However, when looking at the properties & content of the hashtable I can see the following:

16 buckets, but 6 of them have a null key and null value. It's always the same ones.

What could be causing this?

Update:

There's not much code to post:

var settings = new HashTable(16);
settings.Add("eth::address", "192.168.1.1");
//Keep adding the settings mentioned above

No exceptions are thrown, in the end there are 16 items in the hashtable, starting with 3 valid ones, then a few null ones, then a few valid ones, etc....

There's nothing else involved as this is a simply a test case

The tool to inspect is #vs2010

If I try to get one of the values that "got lost", an exception is thrown:

var x = settings["eth::port"];

Will result in:

A first chance exception of type 'System.Exception' occurred in mscorlib.dll
An unhandled exception of type 'System.Exception' occurred in mscorlib.dll


enter code here

Solution

  • To extend Volkan's answer - checking the internal implementation of Hashtable one might find the following:

    public Hashtable(int capacity) : this(capacity, (float) 1f)
    {
    }
    
    public Hashtable(int capacity, float loadFactor)
    {
        // arguments checking - elided
    
        this.loadFactor = 0.72f * loadFactor;
        double num = ((float) capacity) / this.loadFactor;
        if (num > 2147483647.0)
        {
            throw new ArgumentException(
                Environment.GetResourceString("Arg_HTCapacityOverflow"));
        }
        int num2 = (num > 3.0) ? HashHelpers.GetPrime((int) num) : 3;
        this.buckets = new bucket[num2];
        this.loadsize = (int) (this.loadFactor * num2);
        this.isWriterInProgress = false;
    }
    

    So what happens when you initialize it with new Hashtable(16)...? First, num's value is computed to 16/0.72 = 22.(2). Then, HashHelpers.GetPrime(22) kicks in, which looks like this:

    internal static int GetPrime(int min)
    {
        // arguments checking - elided
    
        for (int i = 0; i < primes.Length; i++)
        {
            int num2 = primes[i];
            if (num2 >= min)
            {
                return num2;
            }
        }
    
        // more code; irrelevant in this case - elided
    }
    

    Almost there. We only need to look up what primes is.

    static HashHelpers()
    {
        primes = new int[] { 3, 7, 11, 17, 23 /* more values */ };
    }
    

    With 22 as min argument, we can easily see GetPrime returns 23. And this is the value used in Hashtable constructor to create buckets array. You can perform same analysis for micro framework to see why it creates 16 buckets (which is weird TBH, considering it's good practice for buckets number to be prime value).