6

以下のメソッドを使用して、さまざまな 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;
}

これらは非常に反復的であり、単一の汎用メソッドにラップする方法があるかもしれないことを示唆しています。

私は次のことで行き詰まっていますが、続行する方法や続行する方法を検索する方法がわかりません。

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

どんな助けでも大歓迎です。ありがとう。

==編集== いくつかのコンテキストを追加します。これは、特定のクレジット カード請求会社からポストバックを受け取るリスナー用です。このステップでは検証を行いません。これは、後のビジネス ルールのステップで行われるためです。たとえば、bank_batch_number が int、文字列、または凍結乾燥した齧歯動物のいずれであるかは気にしません。使用していないフィールドをきれいにログに記録できない場合でも、例外で停止するつもりはありません。DB に ext_product_id が存在し、メッセージ内の currency_amount_settled と一致する価格を持っていることは気にしています。そのテストが失敗した場合、トランザクションは保留され、警告がログに記録され、CS スタッフと私に警告が送られます。

ただし、以下で言及されている文化のことは賢明なアドバイスです。

4

4 に答える 4

8

いいえ、メソッドは基本的に完全に分離されています。コンパイラは、それがetcDateTime.TryParseに似ていることを知りません。Int64.TryParse

ターゲット タイプからメソッドへの to マップを作成し、次のように記述できます。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;
}

次のように辞書を設定できます。

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

TryParseパターンは、outパラメーターの値がその型のデフォルト値になることを示しているため、条件付きロジックは必要ないことに注意してください。つまり、反復的な方法でさえも簡単になる可能性があります。

private static Decimal DecimalParse(string value) {
    Decimal result;
    Decimal.TryParse(value, out result);
    return result;
}

余談ですが、デフォルトでは、TryParseパターンはスレッドの現在のカルチャを使用することに注意してください。これが Web サービスからの受信データの解析に使用されている場合は、代わりにインバリアント カルチャを使用することを強くお勧めします。(個人的には、悪いデータを黙って無視するつもりはありませんが、それは意図的なものだと思います。)

于 2013-01-29T19:51:46.860 に答える
2
public delegate bool TryParseDelegate<T>(string str, out T value);

public static T ParseOrDefault<T>(string str, TryParseDelegate<T> parse)
{
    T value;
    return parse(str, out value) ? value : default(T);
}

あなたはそれを次のように呼ぶことができます:

long l = ParseOrDefault<long>("12345", long.TryParse);
于 2013-01-29T19:52:42.100 に答える
2

単純な拡張メソッドを使用しないのはなぜですか?

さまざまな TryParse メソッドからのデフォルトの結果を使用することに関する Jon Skeet の回答は適切です。ただし、拡張メソッドにまだ優れた側面があります。これを頻繁に行う場合は、3 行ではなく 1 行のコードで、呼び出し元のコード (およびオプションで明示的な既定値を指定) で同じことを実行できます。

-- 編集 --元の回答では、基本的に、著者がすでに行っていたのと同じこと を行うわずかに異なる方法を提供しただけであることを認識しています。私は今日、本当に忙しかったときにこれを見つけました。デリゲートとカスタムパーサーは少し多すぎるように見えたので、質問が何であるかを完全に理解するのに時間をかけずに答えを出しまし。ごめん。

(オーバーロードされた) 拡張メソッドとリフレクションを使用する次の例はどうでしょうか? https://stackoverflow.com/a/4740544/618649を参照してください。

警告 Emptor: 私の例では、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;
        }
    }
于 2013-01-30T20:34:36.353 に答える
0

これは私が時々使う方法です。パフォーマンスが主要な懸念事項である場合、これは進むべき道ではありません。try-catchブロックとChangeTypeの呼び出しにより、タイプ固有のTryParseよりもかなり速度が低下するためです。

public static bool TryFromString<T>(string value, out T convertedValue)
{
    Type t = typeof(T);
    convertedValue = default(T);
    if (t.Name == "Nullable`1")
        t = System.Nullable.GetUnderlyingType(t);
    if (value != null)
    {
        try
        {
            convertedValue = (T)System.Convert.ChangeType(value, t, CultureInfo.CurrentCulture);
            return true;
        }
        catch
        {
        }
    }
    return false;
}
于 2013-01-29T19:59:09.277 に答える