Problem
I’d like to do something similar like this:
myYear = record.GetValueOrNull<int?>("myYear"),
It’s worth noting that the generic argument is of the nullable type.
Because the GetValueOrNull method could return null, I tried this first:
public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : class
{
object columnValue = reader[columnName];
if (!(columnValue is DBNull))
{
return (T)columnValue;
}
return null;
}
But now I’m receiving the following error:
Right! It’s a struct! Nullableint> is a struct! So I tried altering the class constraint to a struct constraint (which has the side effect of making it impossible to return null):
public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : struct
Now the assignment:
myYear = record.GetValueOrNull<int?>("myYear");
The following error is displayed:
Is it feasible to specify a nullable type as a generic parameter?
Asked by Tom Pester
Solution #1
Change the return type to NullableT> and use the non nullable parameter when calling the method.
static void Main(string[] args)
{
int? i = GetValueOrNull<int>(null, string.Empty);
}
public static Nullable<T> GetValueOrNull<T>(DbDataRecord reader, string columnName) where T : struct
{
object columnValue = reader[columnName];
if (!(columnValue is DBNull))
return (T)columnValue;
return null;
}
Answered by Greg Dean
Solution #2
public static T GetValueOrDefault<T>(this IDataRecord rdr, int index)
{
object val = rdr[index];
if (!(val is DBNull))
return (T)val;
return default(T);
}
Simply put it like this:
decimal? Quantity = rdr.GetValueOrDefault<decimal?>(1);
string Unit = rdr.GetValueOrDefault<string>(2);
Answered by James Jones
Solution #3
Simply remove the where constraint and modify the last return from return null to return default in your original code (T). You’ll be able to return any type this way.
If you change your if statement to if (columnValue!= DBNull.Value), you can skip using is.
Answered by Robert C. Barth
Solution #4
Disclaimer: This solution works, but it is just meant to be used for educational purposes. 🙂 Here, James Jones’ answer is probably the best, and it’s the one I’d choose.
The dynamic keyword in C# 4.0 makes this even easier, albeit less secure:
public static dynamic GetNullableValue(this IDataRecord record, string columnName)
{
var val = reader[columnName];
return (val == DBNull.Value ? null : val);
}
The explicit type hinting on the RHS is no longer required:
int? value = myDataReader.GetNullableValue("MyColumnName");
In fact, it isn’t required anywhere!
var value = myDataReader.GetNullableValue("MyColumnName");
The value will now be an int, a string, or whatever type the database returned.
The only difficulty is that you can still use non-nullable types on the LHS, in which case you’ll get an ugly runtime exception like:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to 'int' because it is a non-nullable value type
Caveat programmer, as with any programming that employs dynamic.
Answered by Ian Kemp
Solution #5
I believe you want to deal with reference and struct types. It’s what I use to convert XML Element strings to something more typed. With reflection, you may get rid of the nullAlternative. The formatprovider is responsible for handling the ‘.’ or ‘,’ separator in decimals, ints, and doubles, according on the culture. This could work:
public T GetValueOrNull<T>(string strElementNameToSearchFor, IFormatProvider provider = null )
{
IFormatProvider theProvider = provider == null ? Provider : provider;
XElement elm = GetUniqueXElement(strElementNameToSearchFor);
if (elm == null)
{
object o = Activator.CreateInstance(typeof(T));
return (T)o;
}
else
{
try
{
Type type = typeof(T);
if (type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition())
{
type = Nullable.GetUnderlyingType(type);
}
return (T)Convert.ChangeType(elm.Value, type, theProvider);
}
catch (Exception)
{
object o = Activator.CreateInstance(typeof(T));
return (T)o;
}
}
}
You can put it to use in the following way:
iRes = helper.GetValueOrNull<int?>("top_overrun_length");
Assert.AreEqual(100, iRes);
decimal? dRes = helper.GetValueOrNull<decimal?>("top_overrun_bend_degrees");
Assert.AreEqual(new Decimal(10.1), dRes);
String strRes = helper.GetValueOrNull<String>("top_overrun_bend_degrees");
Assert.AreEqual("10.1", strRes);
Answered by Roland Roos
Post is based on https://stackoverflow.com/questions/209160/nullable-type-as-a-generic-parameter-possible