This relates to my Are there any project templates for using T4 to generate classes? question. I have lists of string (nvarchar
) parameter values I need to generate properly typed class properties for.
The values are all primitives - the most complex being DateTime
- in the set { int
, double
, bool
, DateTime
}. Currently I have hand coded typed properties for one - of many - such lists, and to parse the string I start with DateTime.TryParseExact
. If that fails, I try Double.TryParse
, and at the bottom I give up guessing and assume it really is a string.
Is this a fairly sound approach, or are there other more complicated or accurate methods I should rather use?
TryParse(String, Int32) Converts the string representation of a number to its 32-bit signed integer equivalent. A return value indicates whether the conversion succeeded.
The type of this parameter is System.
TryParse(Type, String, Boolean, Object)Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
TryParse(String, NumberStyles, IFormatProvider, Single) Converts the string representation of a number in a specified style and culture-specific format to its single-precision floating-point number equivalent. A return value indicates whether the conversion succeeded or failed.
If the set of values was only [double, DateTime, bool]
this would be an exhaustive but fairly sound method. There is simply so overlap between those sets (true
is always a bool
and 5
is never a bool
).
Having both int
and double
makes this proposition a bit flaky because there is a large amount of overlap. Essentially any int
can be also seen as a double
. How am I to interpret for example 4
if it appears in the value set? It could be interpreted both ways and if values change over time you could see the type of your generated code changing as well.
Consider if a column always had whole numbers simply by coincidence. Hence you generated int
value and wrote a bit of code that depended on them being int
. Then a month or so later a non-whole number was added and suddenly you are spitting out double
values. This would probably have a non-trivial affect on your code.
Personally the approach I would take is to simply have another table which dictated the type of the contents.
Have a look at Convert.ChangeType
and TypeDescriptor.GetConverter
.
I've written an extension method to do this for me:
public static T Convert<T>(this object obj)
{
T result;
try
{
result = (T)System.Convert.ChangeType(obj, typeof(T));
if (object.ReferenceEquals(result, null))
{
var typeConverter = !obj.IsNullable()
? TypeDescriptor.GetConverter(typeof(T))
: new NullableConverter(typeof(T));
result = obj is string
? (T)typeConverter.ConvertFromString(obj as string)
: (T)typeConverter.ConvertTo(obj, typeof(T));
}
}
catch (Exception)
{
result = default(T);
}
return result;
}
public static bool IsNullable<T>(this T obj)
{
return Nullable.GetUnderlyingType(typeof(T)) != null;
}
Usage:
var itemsToConvert = new[] { "4", "5.98", "false", DateTime.Now.ToString() };
var @int = itemsToConvert[0].Convert<int>();
var @double = itemsToConvert[1].Convert<double>();
var @bool = itemsToConvert[2].Convert<bool>();
var @dateTime = itemsToConvert[3].Convert<DateTime>();
Console.WriteLine(@"int: {0}, Type: {1}", @int, @int.GetType());
Console.WriteLine(@"double: {0}, Type: {1}", @double, @double.GetType());
Console.WriteLine(@"bool: {0}, Type: {1}", @bool, @bool.GetType());
Console.WriteLine(@"DateTime: {0}, Type: {1}", @dateTime, @dateTime.GetType());
Output:
int: 4, Type: System.Int32
double: 5.98, Type: System.Double
bool: False, Type: System.Boolean
DateTime: 2013/08/21 06:01:07 PM, Type: System.DateTime
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With