仅当 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 属性的值并获得类似的启动,但是哦,好吧......我注意到它有点晚了。