5

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?

Community
  • 1
  • 1
ProfK
  • 49,207
  • 121
  • 399
  • 775
  • I think there was a topic here before... something like a generic string.Is() method – MS_SP Aug 21 '13 at 14:36
  • 2
    Add the column with type description of the record so you wouldn't have to guess? – BartoszKP Aug 21 '13 at 14:37
  • "0" could be many of these types, how do know wich it really is? – Felipe Pereira Aug 21 '13 at 14:40
  • Is it possible that someone will try to write a double, or date, but use an invalid format, or can you be sure that all inputs that were intended to be non-strings are always properly formatted? – Servy Aug 21 '13 at 14:43
  • what database are you using? If it's SqlDataAdapter then it is possible to get the schema of a datatable (column names and types too). You just need to set sqlDataAdapter1.MissingSchemaAction = MissingSchemaAction.AddWithKey; Then you have dataColumn.DataType (from your related question I'm assuming it's a database-related solution) – Arie Aug 21 '13 at 14:56
  • @Arie, I already have the column data type. It's `nvarchar(60)`. – ProfK Aug 22 '13 at 03:44
  • sory, I misunderstood you. In that case you can use TypeDescriptor instead of TryParse as in here: http://stackoverflow.com/questions/2961656/generic-tryparse but I think the TryParse approach is good too – Arie Aug 22 '13 at 06:24

2 Answers2

3

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.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • Good points. The OP could conceivably still tell the difference between an int and a double by trying to parse into an int first. If the "column" as a whole is supposed to be doubles, but some values would parse to integers, he can differentiate them with his current schema, either by structuring doubles in scientific notation, or by appending some identifier character like "d", "f", "m" as he'd use for a literal in code. – KeithS Aug 21 '13 at 16:10
2

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.

Sameer Singh
  • 1,358
  • 1
  • 19
  • 47