开发者

Calling methods with different signatures from a method that receives a delegate type parameter

开发者 https://www.devze.com 2023-04-09 07:46 出处:网络
I have a generics method (ParseTo) for parsing strings to other types. This method receives a delegate type parameter that contains a method to execute:

I have a generics method (ParseTo) for parsing strings to other types. This method receives a delegate type parameter that contains a method to execute:

public delegate bool ParseToDelegate<T>(string value, out T result);

public static T? ParseTo<T>(this string value, 
    ParseToDelegate<T> method) where T : struct
{
    T result;
    if (String.IsNullOrWhiteSpace(value)) return null;
    if (method(value, out result)) return result;
    return null;
}

That works fine because the signature of TryParse is the same for all the base types.

var s = "1234,567";
Console.WriteLine(s.ParseTo<int>(int.TryParse)); //Error. Returns null
Console.WriteLine(s.ParseTo<decimal>(decimal.TryParse)); //Ok

var d = "14/05/2011 19:45";
Console.WriteLine(d.ParseTo<DateTime>(DateTime.TryParse)); //Ok

var g = Guid.NewGuid().ToString();
Console.Writ开发者_如何学PythoneLine(g.ParseTo<Guid>(Guid.TryParse)); //Ok

My issue is: Now I would like to extend this method for supporting different cultures... But the numeric types and date types have different signatures:

bool TryParse(string s, NumberStyles style, IFormatProvider provider, out int result);
bool TryParse(string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result);

Is there a way to ‘map’ the received delegate and call the correct method? Something like this:

if (typeof(T) == typeof(DateTime))
{
    //Call DateTime.TryParse(string s, IFormatProvider provider, 
        //DateTimeStyles styles, out DateTime result)
}
else
{
    //Call DateTime.TryParse(string s, 
    //NumberStyles style, IFormatProvider provider, out int result);
}


You might be re-inventing the wheel - Convert.ChangeType() does already almost exactly what you want - sample from MSDN:

Temperature cool = new Temperature(5);
Type[] targetTypes = { typeof(SByte), typeof(Int16), typeof(Int32),
                        typeof(Int64), typeof(Byte), typeof(UInt16),
                        typeof(UInt32), typeof(UInt64), typeof(Decimal),
                        typeof(Single), typeof(Double), typeof(String) };
CultureInfo provider = new CultureInfo("fr-FR");

foreach (Type targetType in targetTypes)
{
    try {
    object value = Convert.ChangeType(cool, targetType, provider);
    Console.WriteLine("Converted {0} {1} to {2} {3}.",
                        cool.GetType().Name, cool.ToString(),
                        targetType.Name, value);
    }
    catch (InvalidCastException) {
    Console.WriteLine("Unsupported {0} --> {1} conversion.",
                        cool.GetType().Name, targetType.Name);
    }                     
    catch (OverflowException) {
    Console.WriteLine("{0} is out of range of the {1} type.",
                        cool, targetType.Name);
    }
}


It sounds like you're looking for something similar to what you're already doing. At that point, the easiest thing to do would be to make the second and third parameter generic as well.

public delegate bool ParseToDelegate<T,U,K>(string value, U secondOption, K thirdOption, out T result);

public static T? ParseTo<T, U, K>(this string value, U second, K third, ParseToDelegate<T,U, K> method) where T : struct
{
    T result;
    if (String.IsNullOrWhiteSpace(value)) return null;
    if (method(value, second, third, out result)) return result;
    return null;
}

An issue with that, though, is that the method callsite signature starts to get pretty nasty and it relies heavily on the caller knowing the delegate structure and what the generic parameters are for in the first place.

someDateString.ParseTo<DateTime, IFormatProvider, DateTimeStyles>
            (CultureInfo.CurrentCulture.DateTimeFormat, 
             DateTimeStyles.AssumeUniversal, 
             DateTime.TryParse);

To alleviate that a little, you may want to just wrap those calls in specifically-typed calls and expose those as extension methods instead.

public static DateTime? ParseToDateTime(this string value, IFormatProvider provider, DateTimeStyles style)
{
    return ParseTo<DateTime, IFormatProvider, DateTimeStyles>(value, provider, style, DateTime.TryParse);
}

That will make it easier on the caller, but the underlying guts of the thing may still be a little confusing and should definitely be documented well.

someDateString.ParseToDateTime(CultureInfo.CurrentCulture.DateTimeFormat, 
                               DateTimeStyles.AssumeUniversal);
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号