【发布时间】:2016-06-21 09:27:11
【问题描述】:
我的目标是编写一个弱类型 TryParse 方法,它基本上支持所有可用的结构类型(int、long、float...)
public static bool TryParse(Type type, string s, out object obj)
实现将调用提供的type 参数的TryParse 方法(如果类型为int,将调用int.TryPase,并且输出值将作为对象返回)。
我已经通过反射实现了它,但是有一个重大的性能损失(如我所料)
反射实现:
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;
};
}
}
这是性能测试:
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);
}
}
平均结果是反射调用比直接调用慢大约 200 倍(在 100 次 reties 上)。 Fiddle that demonstrates the reflection test
我尝试通过使用缓存的委托来提高性能:
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;
}
}
我认为使用委托会显着提高性能,因此它会接近直接调用,但我错了,它仍然慢了大约 100 倍(在 100 次重新调用时) Here is the fiddle
我的问题是:
- 虽然我看到了多个将反射调用转换为委托调用的示例,但在这种情况下它不起作用。为什么?
- 在这种情况下有什么方法可以提高性能吗?
【问题讨论】:
-
如果您将此代码的性能与其他代码进行比较,参考其他代码会很有用。当我们不知道更快的代码是什么时,真的很难说“为什么它执行得更慢”。性能差异可能有多种原因。我正在查看您的代码并思考“它使用反射,当然它比不使用反射的版本慢”..
-
我有两个小提琴附加代表的链接:dotnetfiddle.net/RYx0Zt反射:dotnetfiddle.net/LcVqMy
标签: c# performance reflection delegates