Use dynamic data type for TryParse

239 views Asked by At

I have a process that allows users to upload data in Excel file and save to database once the data have gone series of validations . Once such validation is data type validation, to prevent them trying to put a string into integer field, for example. Here is an excerpt of the code. The caller (ValidateContentDataType) calls ValidateDataType() and passes property info and data in string to the callee to perform TryParse.


    public void ValidateContentDataType()
    {
       //Do stuff
       ValidateDataType(typeof(T).GetProperty("nameOfProperty"), data);
       //Do stuff
    }

    private bool ValidateDataType(PropertyInfo propInfo, string dataElement)
    {
        if (propInfo is null) return true;

        if (propInfo.PropertyType == typeof(decimal) || propInfo.PropertyType == typeof(decimal?))
        {
            return decimal.TryParse(dataElement, out decimal temp);
        }

        //Other ifs TryParse for different data type....
        return true;
    }

While this works, I don't like the series of ifs in ValidateDataType(). Instead of series of ifs for different data types, like this:

 if (propInfo.PropertyType == typeof(decimal) || propInfo.PropertyType == typeof(decimal?))
        {
            return decimal.TryParse(dataElement, out decimal temp);
        }

is it possible to have something like this:

  return *propertyType*.TryParse(dataElement, out *propertyType *temp);
2

There are 2 answers

0
Jim Mischel On

Stylistically, I'd probably write that as a switch statement:

switch (propInfo.PropertyType)
{
  case typeof(decimal):
  case typeof(decimal?):
    return decimal.TryParse(dataElement, out decimal temp);
  case typeof(int):
  case typeof(int?):
    return ...
}

I think that looks a lot better than multiple if statements. But I freely admit that it's a subjective opinion.

There might be a solution that involves creating a generic method that expects a type that implements the IParseable interface. Something like:

private bool ValidateDataType<T>(...) where T:IParseable

But I haven't completely worked it out. Might be that you'd have to call the method through reflection.

0
LUAN-I JACKSON On

OK. IParsable gives me what I need. Creating a couple of extensions with the the type parameter T that implements IParsable. Then invoke the extensions so I can use the property types from reflection. Seems to be a lot of work for something seemingly trivia. Anyway, here is what I came up with:

 internal class Program
    {
        static void Main(string[] args)
        {
            Type myClassType = typeof(MyClass);
            PropertyInfo propInfo = myClassType.GetProperty("ForecastYear");
            ValidateDataType(propInfo.PropertyType, "2023");
        }

        static void ValidateDataType(Type propertyType, string input)
        {
            MethodInfo method = typeof(TryParseExtensions).GetMethod(nameof(TryParseExtensions.TryParse));
            MethodInfo generic = method.MakeGenericMethod(propertyType);
            
            object[] args = { input, null };
            var parseSuccessful = (bool)generic.Invoke(null, args);
            var parsedValue = args[1];
        }
    }

    static class TryParseExtensions
    {
        public static T Parse<T>(this string s) where T : IParsable<T>
        {
            return T.Parse(s, null);
        }      

        public static bool TryParse<T>(this string? s, [MaybeNullWhen(false)] out T result) where T : IParsable<T>
        {
            result = default(T);
            if (s == null) { return false; }
            try
            {
                result = s.Parse<T>();
                return true;
            }
            catch { return false; }
        }
    }

    public class MyClass{
        public int ForecastYear { get; set; }
    }