Да, типы, которые можно анализировать из строки, скорее всего, будут иметь статические перегрузки Parse
и TryParse
, которые вы можете найти с помощью отражения, как предложил Джон.
private static Func<string, T> GetParser<T>()
{
// The method we are searching for accepts a single string.
// You can add other types, like IFormatProvider to target specific overloads.
var signature = new[] { typeof(string) };
// Get the method with the specified name and parameters.
var method = typeof(T).GetMethod("Parse", signature);
// Initialize the parser delegate.
return s => (T)method.Invoke(null, new[] { s });
}
Для повышения производительности вы также можете создавать лямбда-выражения, вызывающие их, так как метод Invoke
принимает параметры метода как object[]
, что является ненужным распределением, и если ваши параметры включают типы значений, вызывает бокс. Он также возвращает результат как object
, что также вызывает бокс, когда ваш тип является типом значения.
private static Func<string, T> GetParser<T>()
{
// Get the method like we did before.
var signature = new[] { typeof(string) };
var method = typeof(T).GetMethod("Parse", signature);
// Build and compile a lambda expression.
var param = Expression.Parameter(typeof(string));
var call = Expression.Call(method, param);
var lambda = Expression.Lambda<Func<string, T>>(call, param);
return lambda.Compile();
}
Вызов скомпилированного лямбда-выражения, по сути, так же быстр, как и сам исходный метод синтаксического анализа, но его сборка и компиляция в первую очередь - нет. Вот почему, как и предлагал Джон, мы должны кэшировать полученный делегат.
Я использую статический универсальный класс для кэширования синтаксических анализаторов в ValueString .
private static class Parser<T>
{
public static readonly Func<string, T> Parse = InitParser();
private static Func<string, T> InitParser()
{
// Our initialization logic above.
}
}
После этого ваш метод разбора может быть записан так:
public static T Parse<T>(string s)
{
return Parser<T>.Parse(s);
}