18 July 2010

Creating a light-weight visitor, fluently in C#

In object-oriented programming a common problem is performing some conditional logic based on the type of an object at run-time. For example, one form you may come across is:

public void DoStuff(MemberInfo memberInfo)
{
 EventInfo eventInfo = memberInfo as EventInfo;
 if(eventInfo != null)
 {
  // do something
  return;
 }

 MethodInfo methodInfo = memberInfo as MethodInfo;
 if(methodInfo != null)
 {
  // do something
  return;
 }

 PropertyInfo propertyInfo = memberInfo as PropertyInfo;
 if(propertyInfo != null)
 {
  // do something
  return;
 }

 throw new Exception("Not supported.");
}

Drawbacks to this being you have to wrap the whole thing in a method to make use of the "bomb out" return statements and it's quite a lot of code repetition which, as I’ve talked about previously, I'm not a fan of. Another example is a dictionary type->operation lookup:

// set up some type to operation mappings
static readonly Dictionary<Type, Action<MemberInfo>> operations = new Dictionary<Type, Action<MemberInfo>>();

// probably inside the static constructor...
operations.Add(typeof(EventInfo), memberInfo => 
{
 EventInfo eventInfo = (EventInfo)memberInfo;
 // do somthing 
});
operations.Add(typeof(MethodInfo), memberInfo =>
{
 MethodInfo methodInfo = (MethodInfo)memberInfo;
 // do something
});
operations.Add(typeof(PropertyInfo), memberInfo =>
{
 PropertyInfo propertyInfo = (PropertyInfo)memberInfo;
 // do something
});

// use it like this...
Type type = memberInfo.GetType();
Type matchingType = operations.Keys.FirstOrDefault(t => t.IsAssignableFrom(type));
if(matchingType != null)
{
 operations[matchingType](memberInfo);
}

The major drawback with this method is that you have to use IsAssignableFrom otherwise it doesn't match inherited types. In fact, the above example doesn't work if you just look up the type of memberInfo directly because we'll get types derived from EventInfo etc, not those types themselves. We also still need to cast to the type we want to work with ourselves and enumerating the dictionary isn’t ideal from a performance point of view.

The GoF pattern for solving this is the visitor which I’ve blogged about in the past however this is rather heavy duty, especially if your "do something" is only one line. It is much more performant than the alternatives though, as it’s using low level logic inside the run-time to make the decision about which method to call, so that should be a consideration.

Then next best alternative to the proper visitor is the first ...as...if...return... form but we can wrap it up quite nicely with a couple of extension methods to cut down on the amount of code we have to write. Here’s a trivial example trying to retrieve the parameters for either a method or a property. Depending on the type we need to call a different method so we identify that method using a fluent visitor:

private Type[] GetParamTypes(MemberInfo memberInfo)
{
 Func<ParameterInfo[]> paramGetter = null;

 memberInfo
  .As<MethodInfo>(method => paramGetter = method.GetParameters)
  .As<PropertyInfo>(property => paramGetter = property.GetIndexParameters)
  .As<Object>(o => { throw new Exception("Unsupported member type."); });

 return paramGetter().Select(pi => pi.ParameterType).ToArray();
}

The As extension attempts to cast “this” as the type specified by the type parameter T and if successful calls the supplied delegate. The overload used in the example above will skip the remaining As calls once one has been successful. There is a second overload which takes a Func<T, bool> rather than an Action<T> and will continue to try the next As if false is returned from the Func. The last As call, by specifying Object as the type, is a catch all and allows providing a default implementation or catering for an error case as shown above. The extensions are implemented like so:

/// <summary>
/// Tries to cast an object as type <typeparamref name="T"/> and if successful 
/// calls <paramref name="operation"/>, passing it in.
/// </summary>
/// <typeparam name="T">Type to attempt to cast <paramref name="o"/> as</typeparam>
/// <param name="o"></param>
/// <param name="operation">Operation to be performed if cast is successful</param>
/// <returns>
/// Null if the object cast was successful, 
/// otherwise returns the object for chaining purposes.
/// </returns>
public static object As<T>(this object o, Action<T> operation)
 where T : class
{
 return o.As<T>(obj => { operation(obj); return true; });
}

/// <summary>
/// Tries to cast an object as type <typeparamref name="T"/> and if successful 
/// calls <paramref name="operation"/>, passing it in.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="o"></param>
/// <param name="operation">Operation to be performed if cast is successful, must return 
/// a boolean indicating whether the object was handled.</param>
/// <returns>
/// Null if the object cast was successful and <paramref name="operation"/> returned true, 
/// otherwise returns the object for chaining purposes.
/// </returns>
public static object As<T>(this object o, Func<T, bool> operation)
 where T : class
{
 if (Object.ReferenceEquals(o, null)) return null;

 T t = o as T;
 if (!Object.ReferenceEquals(t, null))
 {
  if (operation(t)) return null;
 }
 return o;
}

14 July 2010

UTC gotchas in .NET and SQL Server

After doing some work with DateTime recently I stumbled across the interesting behaviour that a DateTime which is DateTimeKind.Unspecified will be treated as a DateTimeKind.Local whenever you try to perform some operation upon it. You get an “unspecified” DateTime whenever you don’t explicitly say it is Utc or Local. This makes sense because, when you do the following, in most cases what you intended was to use local time:

DateTime d1 = new DateTime(2010, 07, 01, 12, 0 ,0, 0);

If the current timezone is UTC +01:00 here's what I get when working with the DateTime created above:

d1.Kind; // => Unspecified
d1; // => 01/07/2010 12:00:00
d1.ToUniversalTime(); // => 01/07/2010 11:00:00
TimeZoneInfo.Local.GetUtcOffset(d1); // => 01:00:00

Note it’s applied an offset when calculating the UTC value which as we can see for clarification is +1 hour.

If what we actually wanted was a UTC time we need to explicitly specify the kind e.g.

DateTime d2 = new DateTime(2010, 07, 01, 12, 0 ,0, 0, DateTimeKind.Utc);
DateTime d2 = DateTime.UtcNow;

If you need to work with timezones other than UTC or the system timezone then you'll want to use DateTimeOffset rather than DateTime.

SQL Server and SqlDataReader

Another interesting gotcha arising from this is that the SQL Server datetime data type is also timezone agnostic. Any datetime values retrieved through the SqlDataReader will be an “unspecified” kind DateTime. This means that, even if you're correctly using the C# DateTime.UtcNow or the SQL GETUTCDATE() to produce the values in the database, when you try to retrieve them they will be shifted incorrectly according to the local timezone. Yikes!

There are two ways to deal with this.

DateTime.SpecifyKind()

The first is in C# using DateTime.SpecifyKind():

DateTime d3 = DateTime.SpecifyKind(d1, DateTimeKind.Utc);
d3.Kind; // => Utc
d3; // => 01/07/2010 12:00:00
d3.ToUniversalTime(); // => 01/07/2010 12:00:00

Which could be wrapped up in an extension method for ease of use e.g.

public static class SqlDataReaderExtensions
{
 public static DateTime GetDateTimeUtc(this SqlDataReader reader, string name)
 {
  int fieldOrdinal = reader.GetOrdinal(name);
  DateTime unspecified = reader.GetDateTime(fieldOrdinal);
  return DateTime.SpecifyKind(unspecified, DateTimeKind.Utc);
 }
}

SQL Server 2008 datetimeoffset

If you're using SQL Server 2008 you have the option of using the datetimeoffset data type instead. This will store the +00:00 timezone internally and the SqlDataReader will then retrieve the value correctly as a DateTimeOffset. No need to muck about with Kind.

If you have an existing database using datetime you can CAST these as a datetimeoffset in your query which usefully uses an offset of +00:00 in this case. (It treats "unspecified" as UTC – tut!)