仅当 DLL 可用时才使用库
Use library only if the DLL is available
我在我的一些库中使用 NLog,我希望我的库可以工作,即使我的用户 没有 有 NLog dll.
到目前为止,我的解决方案是为我正在使用的 类 创建一个 ILogger 接口,并在找到 NLog dll 时使用反射从 NLog 调用方法。
问题是这种方法有时非常慢,导致 CPU 在我的情况下使用率增加了 ~10%(有些地方我做了很多日志记录)。
是否有更通用的解决方案来解决我的问题?也许使用 emit 这样我就不会受到这样的性能影响?看来这个问题应该有一个简单的解决办法。
我认为你的解决方案是唯一可能的,那就是使用反射。
事实上,反射在计算方面更昂贵。
我的解决方案与 LogLib (as posted by pepo) 的解决方案类似。
我的解决方案的反射部分导致速度变慢,所以我最终使用 linq 表达式创建了所有记录器方法的编译版本。这样我在启动时的执行时间略有增加,但在整个程序持续时间内执行时间更快(有时以天为单位)。
新版本(更快):
/// <summary>
/// Creates a method with one parameter of type TParam
/// </summary>
private static Action<object, TParam> CreateMethod<TParam>(Type targetType, string methodName)
{
MethodInfo methodInfo = targetType.GetMethod(methodName, new Type[] { typeof(TParam) });
ParameterExpression instance = Expression.Parameter(typeof(object), "inst");
ParameterExpression msg = Expression.Parameter(typeof(TParam), "p");
Expression callExpression = Expression.Call(
Expression.Convert(instance, targetType),
methodInfo,
msg);
Expression<Action<object, TParam>> methodExpression = Expression.Lambda<Action<object, TParam>>(callExpression, instance, msg);
Action<object, TParam> method = methodExpression.Compile();
return method;
}
/// <summary>
/// Creates a getter with the return type TReturn
/// </summary>
private static Func<object, TReturn> CreateGetter<TReturn>(Type targetType, string propertyName)
{
PropertyInfo propertyInfo = targetType.GetProperty(propertyName);
ParameterExpression instance = Expression.Parameter(typeof(object), "inst");
Expression callExpression = Expression.Property(
Expression.Convert(instance, targetType),
propertyInfo);
Expression<Func<object, TReturn>> methodExpression = Expression.Lambda<Func<object, TReturn>>(callExpression, instance);
Func<object, TReturn> property = methodExpression.Compile();
return property;
}
使用这种助手,我存储了方法的编译版本:
Action<object, string> trace = CreateMethod<string>(logger.GetType(), "Trace");
Func<object, bool> isTraceEnabled = CreateGetter<bool>(logger.GetType(), "IsTraceEnabled");
在我的包装器中,我这样调用方法:
isTraceEnabled(logger);
trace(logger, "message")
这比旧版本复杂得多,我只是将记录器声明为动态
dynamic logger = ....
// ...
logger.IsTraceEnabled;
logger.Trace("message");
但在我的特殊情况下,加速是显而易见的。现在我只有大约 1% 的减速。我做了一些更多的测量,结果发现背后的真正原因是检查是否启用了 Trace 的调用次数过多。在发布版本中,Trace 被关闭,但我仍然有大约 14 个记录器是 IsTraceEnabled 属性 我必须不断探测。
事后看来,我本可以缓存 IsEnabled 属性的值并获得类似的启动,但是哦,好吧......我注意到它有点晚了。
我在我的一些库中使用 NLog,我希望我的库可以工作,即使我的用户 没有 有 NLog dll.
到目前为止,我的解决方案是为我正在使用的 类 创建一个 ILogger 接口,并在找到 NLog dll 时使用反射从 NLog 调用方法。
问题是这种方法有时非常慢,导致 CPU 在我的情况下使用率增加了 ~10%(有些地方我做了很多日志记录)。
是否有更通用的解决方案来解决我的问题?也许使用 emit 这样我就不会受到这样的性能影响?看来这个问题应该有一个简单的解决办法。
我认为你的解决方案是唯一可能的,那就是使用反射。 事实上,反射在计算方面更昂贵。
我的解决方案与 LogLib (as posted by pepo) 的解决方案类似。
我的解决方案的反射部分导致速度变慢,所以我最终使用 linq 表达式创建了所有记录器方法的编译版本。这样我在启动时的执行时间略有增加,但在整个程序持续时间内执行时间更快(有时以天为单位)。
新版本(更快):
/// <summary>
/// Creates a method with one parameter of type TParam
/// </summary>
private static Action<object, TParam> CreateMethod<TParam>(Type targetType, string methodName)
{
MethodInfo methodInfo = targetType.GetMethod(methodName, new Type[] { typeof(TParam) });
ParameterExpression instance = Expression.Parameter(typeof(object), "inst");
ParameterExpression msg = Expression.Parameter(typeof(TParam), "p");
Expression callExpression = Expression.Call(
Expression.Convert(instance, targetType),
methodInfo,
msg);
Expression<Action<object, TParam>> methodExpression = Expression.Lambda<Action<object, TParam>>(callExpression, instance, msg);
Action<object, TParam> method = methodExpression.Compile();
return method;
}
/// <summary>
/// Creates a getter with the return type TReturn
/// </summary>
private static Func<object, TReturn> CreateGetter<TReturn>(Type targetType, string propertyName)
{
PropertyInfo propertyInfo = targetType.GetProperty(propertyName);
ParameterExpression instance = Expression.Parameter(typeof(object), "inst");
Expression callExpression = Expression.Property(
Expression.Convert(instance, targetType),
propertyInfo);
Expression<Func<object, TReturn>> methodExpression = Expression.Lambda<Func<object, TReturn>>(callExpression, instance);
Func<object, TReturn> property = methodExpression.Compile();
return property;
}
使用这种助手,我存储了方法的编译版本:
Action<object, string> trace = CreateMethod<string>(logger.GetType(), "Trace");
Func<object, bool> isTraceEnabled = CreateGetter<bool>(logger.GetType(), "IsTraceEnabled");
在我的包装器中,我这样调用方法:
isTraceEnabled(logger);
trace(logger, "message")
这比旧版本复杂得多,我只是将记录器声明为动态
dynamic logger = ....
// ...
logger.IsTraceEnabled;
logger.Trace("message");
但在我的特殊情况下,加速是显而易见的。现在我只有大约 1% 的减速。我做了一些更多的测量,结果发现背后的真正原因是检查是否启用了 Trace 的调用次数过多。在发布版本中,Trace 被关闭,但我仍然有大约 14 个记录器是 IsTraceEnabled 属性 我必须不断探测。
事后看来,我本可以缓存 IsEnabled 属性的值并获得类似的启动,但是哦,好吧......我注意到它有点晚了。