19

厳密に型指定されたプロパティを持つオブジェクトのリストに CSV ファイルを解析しています。これには、を使用して、ファイルの各文字列値をIConvertible型 ( int、、、など)decimalに解析することが含まれます。 doubleDateTimeTypeDescriptor

try catch解析が失敗した場合の状況を処理するために を使用しています。この例外が発生した場所と理由の正確な詳細は、さらに調査するためにログに記録されます。以下は、実際の解析コードです。

try
{
    parsedValue = TypeDescriptor.GetConverter(type).ConvertFromString(dataValue);
}
catch (Exception ex)
{
    // Log failure
}

問題:

値が正常に解析されると、プロセスは迅速になります。多数の無効なデータを含むデータを解析する場合、プロセスは何千倍も遅くなる可能性があります (例外をキャッチするため)。

への解析でこれをテストしてきましたDateTime。パフォーマンスの数値は次のとおりです。

  • 成功した解析: 解析ごとに平均32ティック
  • 解析の失敗: 解析あたり平均146296 ティック

これは 4500 倍以上遅いです。

質問:

try catch高価なメソッドを使用せずに、文字列値を正常に解析できるかどうかを確認することはできますか? それとも、これを行うべき別の方法がありますか?

編集:型は実行時に決定されるため、使用する必要がありますTypeDescriptor(使用しないでください)。DateTime.TryParse

4

5 に答える 5

13

変換するタイプの既知のセットがある場合は、一連のif/elseif/elseif/else(またはswitch/caseタイプ名に対して) を実行して、本質的にそれを特殊な解析メソッドに配布できます。これはかなり速いはずです。これは、@ Fabio's answerで説明されているとおりです。

それでもパフォーマンスの問題がある場合は、サポートする必要がある新しい解析メソッドを追加できるルックアップ テーブルを作成することもできます。

いくつかの基本的な解析ラッパーが与えられた場合:

public delegate bool TryParseMethod<T>(string input, out T value);

public interface ITryParser
{
    bool TryParse(string input, out object value);
}

public class TryParser<T> : ITryParser
{
    private TryParseMethod<T> ParsingMethod;

    public TryParser(TryParseMethod<T> parsingMethod)
    {
        this.ParsingMethod = parsingMethod;
    }

    public bool TryParse(string input, out object value)
    {
        T parsedOutput;
        bool success = ParsingMethod(input, out parsedOutput);
        value = parsedOutput;
        return success;
    }
}

次に、ルックアップを実行して適切なパーサーを呼び出す変換ヘルパーをセットアップできます。

public static class DataConversion
{
    private static Dictionary<Type, ITryParser> Parsers;

    static DataConversion()
    {
        Parsers = new Dictionary<Type, ITryParser>();
        AddParser<DateTime>(DateTime.TryParse);
        AddParser<int>(Int32.TryParse);
        AddParser<double>(Double.TryParse);
        AddParser<decimal>(Decimal.TryParse);
        AddParser<string>((string input, out string value) => {value = input; return true;});
    }

    public static void AddParser<T>(TryParseMethod<T> parseMethod)
    {
        Parsers.Add(typeof(T), new TryParser<T>(parseMethod));
    }

    public static bool Convert<T>(string input, out T value)
    {
        object parseResult;
        bool success = Convert(typeof(T), input, out parseResult);
        if (success)
            value = (T)parseResult;
        else
            value = default(T);
        return success;
    }

    public static bool Convert(Type type, string input, out object value)
    {
        ITryParser parser;
        if (Parsers.TryGetValue(type, out parser))
            return parser.TryParse(input, out value);
        else
            throw new NotSupportedException(String.Format("The specified type \"{0}\" is not supported.", type.FullName));
    }
}

次に、使用法は次のようになります。

//for a known type at compile time
int value;
if (!DataConversion.Convert<int>("3", out value))
{
    //log failure
}

//or for unknown type at compile time:
object value;
if (!DataConversion.Convert(myType, dataValue, out value))
{
    //log failure
}

これにより、おそらくジェネリックが拡張されて、objectボクシングと型キャストが回避される可能性がありますが、現状ではこれで問題なく動作します。おそらく、測定可能なパフォーマンスがある場合にのみ、その側面を最適化してください。

編集:DataConversion.Convert指定されたコンバーターが登録されていない場合は、メソッドにフォールバックするTypeConverterか、適切な例外をスローできるように、メソッドを更新できます。キャッチオールを使用するか、定義済みのサポートされているタイプのセットを使用して、最初からtry/catchやり直すことを避けるかは、あなた次第です。NotSupportedException現状では、サポートされていない型を示すメッセージとともにをスローするようにコードが更新されています。意味があるので自由に微調整してください。パフォーマンスに関しては、最も一般的に使用される型に特化したパーサーを指定すると、キャッチオールを実行することが理にかなっている可能性があります。

于 2013-05-30T12:50:16.563 に答える
2

メソッドを使用できますTryParse

if (DateTime.TryParse(input, out dateTime))
{
    Console.WriteLine(dateTime);
}
于 2013-05-30T12:11:46.823 に答える
2

Parse を呼び出す前に、型ごとに正規表現を作成し、それを文字列に適用するのはどうですか? 文字列が一致しない場合は解析されないように、正規表現を作成する必要があります。正規表現テストを実行する必要があるため、文字列が解析されると少し遅くなりますが、解析されない場合ははるかに高速になります。

正規表現文字列を に入れることができますDictionary<Type, string>。これにより、使用する正規表現文字列を簡単に決定できます。

于 2013-05-30T12:48:37.180 に答える
1

場合によります。DateTime を使用している場合は、いつでもTryParse関数を使用できます。これにより、大幅に高速化されます。

于 2013-05-30T12:11:38.073 に答える