C#中TryParse()的通用包装器

Sno*_*ash 6 c# generics

我正在使用以下方法提供对各种Type类的TryParse()方法的快速内联访问.基本上我希望能够解析来自Web服务的字符串(如果可能),否则返回默认值.

private Int64 Int64Parse(string value) {
    Int64 result;
    if (!Int64.TryParse(value, out result)) { return default(Int64); }
    return result;
}

private DateTime DateTimeParse(string value) {
    DateTime result;
    if (!DateTime.TryParse(value, out result)) { return default(DateTime); }
    return result;
}

private Decimal DecimalParse(string value) {
    Decimal result;
    if (!Decimal.TryParse(value, out result)) { return default(Decimal); }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

这些是非常重复的,对我来说,可能有一种方法可以将它们包装成单个通用方法.

我坚持以下但不确定如何继续或如何搜索如何继续.

private T ParseString<T>(string value) {
    T result;
    if (!T.TryParse(value, out result)) { return default(T); }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

任何帮助,将不胜感激.谢谢.

==编辑==添加一些上下文.这是为了收听来自特定信用卡结算公司的回发的听众.我没有在此步骤进行验证,因为这是在稍后的业务规则步骤中完成的.例如,我不在乎bank_batch_number是作为int,string还是冻干的啮齿动物进入; 如果我不能干净地记录我不使用的字段,我不会停止异常.我关心ext_product_id存在于我们的数据库中,并且在消息中具有与currency_amount_settled相匹配的价格; 如果该测试失败,则交易被暂停,将记录警告,我们的CS员工和我自己将收到警报.

下面提到的文化事物是圣人的建议.

Jon*_*eet 7

不,这些方法基本上是完全分开的 - 编译器不知道它DateTime.TryParse与之类似Int64.TryParse.

可以创建一个Dictionary<Type, Delegate>从目标类型到方法的映射,然后编写如下内容:

private delegate bool Parser<T>(string value, out T result);

private T Parse<T>(string value) where T : struct
{
    // TODO: Validate that typeof(T) is in the dictionary
    Parser<T> parser = (Parser<T>) parsers[typeof(T)];
    T result;
    parser(value, out result);
    return result;
}
Run Code Online (Sandbox Code Playgroud)

您可以像这样填充字典:

static readonly Dictionary<Type, Delegate> Parsers = CreateParsers();    

static Dictionary<Type, Delegate> CreateParsers()
{
    var parsers = new Dictionary<Type, Delegate>();
    AddParser<DateTime>(parsers, DateTime.TryParse);
    AddParser<Int64>(parsers, Int64.TryParse);
    return parsers;
}

static void AddParser<T>(Dictionary<Type, Delegate> parsers, Parser<T> parser)
{
    parsers[typeof(T)] = parser;
}
Run Code Online (Sandbox Code Playgroud)

请注意,该TryParse模式表明out参数的值无论如何都是该类型的默认值,因此您不需要条件逻辑.这意味着即使您的重复方法也可以变得更简单:

private static Decimal DecimalParse(string value) {
    Decimal result;
    Decimal.TryParse(value, out result);
    return result;
}
Run Code Online (Sandbox Code Playgroud)

另外,请注意,默认情况下,TryParse模式将使用线程的当前文化.如果这是用于解析来自Web服务的传入数据,我强烈建议您使用不变文化.(我个人也不会默默地忽略不良数据,但我认为这是故意的.)


Cra*_*aig 2

为什么不直接使用简单的扩展方法呢?

Jon Skeet 关于仅使用各种 TryParse 方法的默认结果的回答很好。不过,扩展方法仍然一个很好的方面。如果您经常这样做,您可以在调用代码中用一行代码而不是三行完成相同的事情(加上可选地指定显式默认值)。

- 编辑 -确实意识到,在我原来的答案中,我基本上只是提供了一种稍微不同的方法来完成作者已经在做的同样的事情。今天早些时候,当我真的很忙时,我发现了这个问题,认为委托和自定义解析器的东西看起来可能有点多,然后在没有真正花时间完全理解问题是什么的情况下给出了答案。对不起。

下面使用(重载的)扩展方法和反射怎么样?参考/sf/answers/331838111/

买者自负:我的示例没有考虑到您尝试转换没有 TryParse 方法的类型。围绕调用应该有一些异常处理GetMethod,等等。

/* The examples generates this output when run:

0
432123
-1
1/1/0001 12:00:00 AM
1/1/1970 12:00:00 AM
1/30/2013 12:00:00 PM
-1
12342.3233443

*/


class Program
    {
    static void Main ( string[] args )
        {
        Debug.WriteLine( "blah".Parse<Int64>() );
        Debug.WriteLine( "432123".Parse<long>() );
        Debug.WriteLine( "123904810293841209384".Parse<long>( -1 ) );

        Debug.WriteLine( "this is not a DateTime value".Parse<DateTime>() );
        Debug.WriteLine( "this is not a DateTime value".Parse<DateTime>( "jan 1, 1970 0:00:00".Convert<DateTime>() ) );
        Debug.WriteLine( "2013/01/30 12:00:00".Parse<DateTime>() );

        Debug.WriteLine( "this is not a decimal value".Parse<decimal>( -1 ) );
        Debug.WriteLine( "12342.3233443".Parse<decimal>() );
        }
    }

static public class Extensions
    {
    static private Dictionary<Type,MethodInfo> s_methods = new Dictionary<Type, MethodInfo>();

    static public T Parse<T> ( this string value ) where T : struct
        {
        return value.Parse<T>( default( T ) );
        }

    static public T Parse<T> ( this string value, T defaultValue ) where T : struct
        {
        // *EDITED* to cache the Reflection lookup--NOT thread safe
        MethodInfo m = null;
        if ( s_methods.ContainsKey( typeof( T ) ) )
            {
            m = s_methods[ typeof( T ) ];
            }
        else
            {
            m = typeof( T ).GetMethod(
                 "TryParse"
                 , BindingFlags.Public | BindingFlags.Static
                 , Type.DefaultBinder
                 , new[] { typeof( string ), typeof( T ).MakeByRefType() }
                 , null
                 );
            s_methods.Add( typeof( T ), m );
            }

        var args = new object[] { value, null };
        if( (bool)m.Invoke( null, args ))
            {
            return (T) args[ 1 ];
            }
        return defaultValue;
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • @SnowCrash:您“真的”想在每次转换上使用反射吗?对我来说这听起来不是一个好主意。显然,您仍然可以更改我的方法以使用扩展方法作为公共 API - 但我个人会坚持使用“注册一堆委托”方法来进行实际转换。 (2认同)