'Build an expression tree that can handle any type/class with a "TryParse()" method

i have the following method that returns a "value parser" delegate according to the input type. it works fine but i'd like to get rid of the switch statements and type checks and be able to return a value parser delegate for any type that has a TryParse() method.

internal static Func<object?, (bool isSuccess, object value)>? ValueParser(this Type type)
{
    type = Nullable.GetUnderlyingType(type) ?? type;

    if (type.IsEnum)
        return input => (Enum.TryParse(type, ToString(input), out var res), res!);

    switch (Type.GetTypeCode(type))
    {
        case TypeCode.String:
            return input => (true, input!);

        case TypeCode.Boolean:
            return input => (bool.TryParse(ToString(input), out var res), res);

        case TypeCode.DateTime:
            return input => (DateTime.TryParse(ToString(input), out var res), res);

        //other supported types go here...

        case TypeCode.Object:
            if (type == Types.Guid)
            {
                return input => (Guid.TryParse(ToString(input), out var res), res);
            }            
            else if (type == Types.TimeSpan)
            {
                return input => (TimeSpan.TryParse(ToString(input), out var res), res!);
            }
            break;
    }

    return null; //unsupported types will cause a null return

    static string? ToString(object? value)
    {
        if (value is string x)
            return x;

        return value?.ToString();
    }
}

i believe the solution would be to build an expression tree something like below. but i haven't got the slightest clue how to go about building an expression tree correctly. so far all i have is the following:

internal static Func<object?, (bool isSuccess, object value)>? ValueParser(this Type type)
{
    type = Nullable.GetUnderlyingType(type) ?? type;

    var inputParam = Expression.Parameter(typeof(string), "input");

    if (type == Types.String)
    {
        //no need for conversion if input type is string. so delegate should simply return a tuple (true,"inputValue").
        var returnTarget = Expression.Label(type);
        var returnCall = Expression.Return(returnTarget, inputParam);
    }

    var parsedVar = Expression.Variable(type, "parsed");

    var tryParseCall = Expression.Call(
        type,
        "TryParse",
        null,
        inputParam,
        parsedVar);

    //need to compile the expression and return here.
    //if the input type doesn't have a TryParse() method, null should be returned.
    //also not sure if we need Expression.Convert() to handle value types.
}

i've been banging my head against the wall on this for a few days without much success. would really appreciate any help you can provide. thanks!



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source