As Markus pointed out, you've got two challenges here. One is about structs vs value types and the other one is to do with the fact that you have to deal with null values.
The second one is trivial to tackle: Just add a ?
to your implementation like this: row.Field<T?>(columnName)
and your exceptions will be gone.
The first problem, however, is a nasty and frequently encountered one. I am unaware of a pretty way of solving this. Let me still suggest something:
Based on your code above I assume that you are happy to get back Nullable types even for non-nullable columns. So here is something you could do to support reference types on top of what you have and still avoid too much code duplication:
// value type version
public static T? FieldValueStruct<T>(this DataRow row, string columnName, bool checkColumn = false)
where T : struct
{
return row.GetValue(columnName, checkColumn, default(T), row.Field<T? /*with a question mark!!!*/ >);
}
// reference type version
public static T FieldValueClass<T>(this DataRow row, string columnName, bool checkColumn = false)
where T : class
{
return row.GetValue(columnName, checkColumn, default(T), row.Field<T>);
}
// shared amongst value and reference type implementation
private static T GetValue<T>(this DataRow row, string columnName, bool checkColumn, T defaultValue, Func<string, T> getter)
{
return checkColumn && !row.Table.Columns.Contains(columnName)
? defaultValue
: getter(columnName);
}
With this code in place, you get the functionality you want but at a price: You will need to specify type parameters (just like you do now) when you call these methods because type inference won't work (here is why).
string s;
// no type inference, type parameter must be specified
s = row.FieldValueClass<string>("test");
Also, you will need to differentiate in your calls between the value type version and the reference type version which simply isn't pretty. Why do we need to use two different names for the methods? The reason for that is that you cannot overload methods by simply adding different type constraints (see here).
The type inference topic could be solved by using an out parameter which, however, comes again with a bunch of downsides...
// value type version that works with type inference
public static void FieldValueStruct<T>(this DataRow row, string columnName, out T? value, bool checkColumn = false)
where T : struct
{
value = row.GetValue(columnName, checkColumn, default(T), row.Field<T?>);
}
// reference type version that works with type inference
public static void FieldValueClass<T>(this DataRow row, string columnName, out T value, bool checkColumn = false)
where T : class
{
value = row.GetValue(columnName, checkColumn, default(T), row.Field<T>);
}
Now, you can call your method without the type parameter like this:
string s;
// with type inference, doesn't work with properties, though, only with fields
row.FieldValueClass("test", out s);
Unfortunately, this does not work with properties - only with fields.
You see, the world is evil and, sometimes, we cannot do too much about it. ;)
Update based on your comment:
The code below changes your semantics a little but perhaps that's ok:
public static T FieldValue<T>(this DataRow row, string columnName, bool checkColumn = false)
{
return checkColumn && !row.Table.Columns.Contains(columnName)
? default(T)
: row.Field<T>(columnName);
}
Calling this method would need to look like:
// this will return 0 if the column is not available, a null value from the database will cause an exception
int i = r.FieldValue<int>("test");
// this will return null if the column is not available, a null value from the database would be ok
int? iNullable = r.FieldValue<int?>("test");
// this will return null if the column is not available, a null value from the database would be ok
string s = r.FieldValue<string>("test");