The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 4,224 other subscribers

.NET/C#: refactoring some C# 1 code that uses HashTables as a poor mans property bag (via:Stack Overflow)

Posted by jpluimers on 2014/01/30

A while ago, I was refactoring some C# 1 code that uses HashTables as a poor mans property bag.

The problem was that I felt my code was convoluted, and should be denser, especially avoiding Convert.ChangeType. My code was already much simpler than casting tuples to a superclass.

So I asked this question on StackOverflow: c# – Is there a solution that feels less clumsy than Convert.ChangeType to get the value from a HashTable – Stack Overflow.

User dasblinkenlight showed it could be shortened and explained why (hyperlinks are mine):

Since System.String is sealed, the expression

genericType.IsSubclassOf(stringType)

is the same as

genericType == stringType

Therefore you do not need a call of Convert.ChangeType: you can cast to T by casting to object, like this:

object stringResult; // Note the change of type to "object"
if (haveValue)
    stringResult = ((string)properties[propertyName]).Trim();
else
    stringResult = string.Empty;
result = (T)stringResult; // It is allowed to cast object to generic T

The original .NET 1.1 code had loads of null checks wrapped if/then/else statements to assign default values for null values.
I wanted to get rid of that, and get code like this:

using System;
using System.Collections;
using System.Collections.Generic;

using BeSharp.Collections;
using BeSharp.Generic;

namespace HashTableExtensionsDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Hashtable properties = new Hashtable();
            properties["int"] = 21;
            properties["bool"] = false;
            properties["double"] = 3.14;
            properties["string"] = "foo";
            properties["null"] = null;

            // get values that are not null
            int i = properties.LoadValue("int");
            bool b = properties.LoadValue("bool");
            double d = properties.LoadValue("double");
            string s = properties.LoadValue("string");

            printValues(i, d, s);

            // convert null values to a specified default
            i = properties.LoadValue("null", -i);
            b = properties.LoadValue("bool", !b);
            d = properties.LoadValue("null", -d);
            s = properties.LoadValue("null", "empty");

            printValues(i, d, s);

            // convert null values to the default for the type
            i = properties.LoadValue("null");
            b = properties.LoadValue("bool");
            d = properties.LoadValue("null");
            s = properties.LoadValue("null");

            printValues(i, d, s);
        }

        private static void printValues(int i, double f, string s)
        {
            IList nameSeparatorValues = Reflector.GetNameSeparatorValues(new { i, f, s });
            Console.WriteLine(string.Join(Environment.NewLine, nameSeparatorValues));
        }
    }
}

The demo code has this output, showing that it works in three cases:

  • property has  a value
  • property is null, default is applied
  • property is null, default(T) is applied

i:21
f:3.14
s:foo
i:-21
f:-3.14
s:empty
i:0
f:0
s:

I changed the code since the StackOverflow question to make it clear.
It now consists of three methods:

  • A generic T LoadValue without a default; it will give you the default(T) when the obtained property is null.
  • A specific string LoadValue where you specify what happens with a null value: choose between returning a null string or String.Empty.
  • A generic T LoadValue where you can pass your own default value that is returned when the obtained property is null.

Enjoy (:

–jeroen

using System;
using System.Collections;

namespace BeSharp.Collections
{
    public static class HashTableExtensions
    {
        public static T LoadValue(this Hashtable properties, string propertyName)
        {
            T defaultValue = default(T);
            T result = LoadValue(properties, propertyName, defaultValue);
            return result;
        }

        public static string LoadValue(this Hashtable properties, string propertyName, bool nullStringAllowed)
        {
            string defaultValue;
            if (nullStringAllowed)
                defaultValue = null;
            else
                defaultValue = string.Empty;
            string result = LoadValue(properties, propertyName, defaultValue);
            return result;
        }

        public static T LoadValue(this Hashtable properties, string propertyName, T defaultValue)
        {
            T result;
            bool haveValue = properties[propertyName] != null;

            Type genericType = typeof(T);
            Type stringType = typeof(string);
            if (genericType == stringType)
            {
                object stringResult;
                if (haveValue)
                    stringResult = ((string)properties[propertyName]).Trim();
                else
                    stringResult = defaultValue;
                result = (T)stringResult;
            }
            else
            {
                if (haveValue)
                    result = (T)(properties[propertyName]);
                else
                    result = defaultValue;
            }
            return result;
        }
    }
}

–jeroen

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

 
%d bloggers like this: