.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 == stringTypeTherefore 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 LoadValuewithout a default; it will give you the default(T) when the obtained property is null. - A specific
string LoadValuewhere you specify what happens with a null value: choose between returning a null string or String.Empty. - A generic
T LoadValuewhere 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 comment