从接收委托类型参数的方法调用具有不同签名的方法

时间:2011-09-30 12:43:55

标签: c# generics delegates

我有一个泛型方法(ParseTo),用于将字符串解析为其他类型。此方法接收包含要执行的方法的委托类型参数:

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;
}

这很好,因为TryParse的签名对于所有基类型都是相同的。

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.WriteLine(g.ParseTo<Guid>(Guid.TryParse)); //Ok

我的问题是:现在我想扩展这种方法以支持不同的文化...但数字类型和日期类型有不同的签名:

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

有没有办法'映射'收到的委托并调用正确的方法?像这样:

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);
}

2 个答案:

答案 0 :(得分:0)

您可能正在重新发明轮子 - Convert.ChangeType()几乎完全符合您的要求 - 来自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);
    }
}

答案 1 :(得分:0)

听起来你正在寻找类似于你已经在做的事情。在这一点上,最简单的方法是使第二个和第三个参数也是通用的。

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;
}

然而,问题在于,方法callsite签名开始变得非常讨厌它在很大程度上依赖于调用者知道委托结构以及通用参数的用途。

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

为了减轻这一点,您可能只想将这些调用包装在特定类型的调用中,而是将它们作为扩展方法公开。

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

这将使调用者更容易,但事情的根本内容可能仍然有点令人困惑,应该很好地记录下来。

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