My goal is to write a weakly typed TryParse
method that is going support basically all the available struct types (int, long, float...)
public static bool TryParse(Type type, string s, out object obj)
The implementation is going to invoke the TryParse
method of the provided type
argument (if type is int
, int.TryPase
will be invoked and the out value is going to be returned as object).
I've implemented it via reflection, but there is a major performance penalty (as I expected)
The Reflection Implementation:
public static class ParserHelper
{
public delegate bool TryParseDl(string str, out object obj);
private static readonly HashSet<Type> ParsableStructs = new HashSet<Type>
{
typeof(int),
typeof(uint),
typeof(decimal),
typeof(short),
typeof(ushort),
typeof(double),
typeof(long),
typeof(ulong),
typeof(float),
typeof(byte),
typeof(sbyte)
};
public static readonly ReadOnlyDictionary<Type, TryParseDl> StructParsers;
static ParserHelper()
{
StructParsers = new ReadOnlyDictionary<Type, TryParseDl>(CreateParsersForStructs());
}
/// Creates parsers for structs
private static Dictionary<Type, TryParseDl> CreateParsersForStructs()
{
var parsers = new Dictionary<Type, TryParseDl>();
foreach (var t in ParsableStructs)
{
parsers[t] = GetParserForStruct(t);
}
return parsers;
}
private static TryParseDl GetParserForStruct(Type targetType)
{
var methodInfo = targetType.GetMethod(
"TryParse",
BindingFlags.Public | BindingFlags.Static,
Type.DefaultBinder,
new[] { typeof(string), targetType.MakeByRefType() },
null);
return (string str, out object obj) =>
{
if (string.IsNullOrEmpty(str))
{
obj = targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
return true;
}
var inputParameters = new object[] { str, null };
var tryParseResult = (bool)methodInfo.Invoke(null, inputParameters);
obj = inputParameters[1];
return tryParseResult;
};
}
}
And here is the performance test:
public class Program
{
public static void Main()
{
Stopwatch s = new Stopwatch();
string str = "100";
s.Start();
for(int j = 0;j<100;j++)
{
int i;
int.TryParse(str,out i);
}
s.Stop();
Console.WriteLine(s.Elapsed);
s.Reset();
s.Start();
var parser = ParserHelper.StructParsers[typeof(int)];
for(int j = 0;j<100;j++)
{
object o;
parser(str, out o);
}
s.Stop();
Console.WriteLine(s.Elapsed);
}
}
The average result is that reflection invocation is a bout 200 times slower than direct invocation (on 100 reties). Fiddle that demonstrates the reflection test
I tried to improve performance by using a cached delegates:
public static class StructParserExtensions
{
public static bool IntToObjParse(string str, out object obj)
{
int i;
var result = int.TryParse(str, out i);
obj = result ? (object)i : null;
return result;
}
public static bool LongToObjParse(string str, out object obj)
{
long i;
var result = long.TryParse(str, out i);
obj = result ? (object)i : null;
return result;
}
//implementations for other types goes here
}
public static class ParserHelper
{
public delegate bool TryParseDl(string str, out object obj);
public static readonly ReadOnlyDictionary<Type, TryParseDl> StructParsers;
static ParserHelper()
{
StructParsers = new ReadOnlyDictionary<Type, TryParseDl>(CreateParsersForStructs());
}
/// Creates parsers for structs
/// </summary>
/// <returns>Dictionary</returns>
private static Dictionary<Type, TryParseDl> CreateParsersForStructs()
{
var parsers = new Dictionary<Type, TryParseDl>();
parsers[typeof(int)] = StructParserExtensions.IntToObjParse;
parsers[typeof(long)] = StructParserExtensions.LongToObjParse;
return parsers;
}
}
I assumed that using delegates will drastically improve the performance so it will be close to the direct invocation, but I was wrong it is still about 100 times slower (on 100 reties) Here is the fiddle My questions are:
- While I saw multiple examples where converting reflection calls into delegate calls makes the difference, it doesn't do the job in this case. Why?
- Is there any way to improve the performance in this case?
Aucun commentaire:
Enregistrer un commentaire