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

  • Email Subscription

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

    Join 716 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:

[sourcecode language=”csharp”]
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
[/sourcecode]

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:

[sourcecode language=”csharp”]
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));
}
}
}
[/sourcecode]

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

[sourcecode language=”csharp”]
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;
}
}
}
[/sourcecode]

–jeroen

Leave a Comment

 

Discover more from The Wiert Corner - irregular stream of stuff

Subscribe now to keep reading and get access to the full archive.

Continue reading