我需要测量我的 winform .NET 应用程序中每种方法的执行持续时间。输出到文件中。
我不需要为每个 .NET 方法设置代码开始时间和结束时间,我想知道是否有一个库可以通过更改配置文件来打开/关闭它。
谢谢。
科龙克斯
您可以使用 C# 的 AOP 库,如postsharp。
postsharp 文档中的修改示例:
/// <summary>
/// Aspect that, when applied on a method, emits a trace message before and
/// after the method execution.
/// </summary>
[Serializable]
public class TraceAttribute : OnMethodBoundaryAspect
{
private string methodName;
private DateTime startTime;
/// <summary>
/// Method executed at build time. Initializes the aspect instance. After the execution
/// of <see cref="CompileTimeInitialize"/>, the aspect is serialized as a managed
/// resource inside the transformed assembly, and deserialized at runtime.
/// </summary>
/// <param name="method">Method to which the current aspect instance
/// has been applied.</param>
/// <param name="aspectInfo">Unused.</param>
public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
{
this.methodName = method.DeclaringType.FullName + "." + method.Name;
}
/// <summary>
/// Method invoked before the execution of the method to which the current
/// aspect is applied.
/// </summary>
/// <param name="args">Unused.</param>
public override void OnEntry(MethodExecutionArgs args)
{
startTime = DateTime.Now;
Trace.TraceInformation("{0}: Enter", this.methodName);
Trace.Indent();
}
/// <summary>
/// Method invoked after successfull execution of the method to which the current
/// aspect is applied.
/// </summary>
/// <param name="args">Unused.</param>
public override void OnSuccess(MethodExecutionArgs args)
{
Trace.Unindent();
var duration = DateTime.Now - startTime;
Trace.TraceInformation("{0}: Success, Duration: {1}ms", this.methodName, duration.TotalMilliseconds);
}
}
要将其应用于项目中的每个方法,您只需编辑 assemblyinfo.cs 并添加:
[assembly: SomeNamespace.TraceAttribute()]
这样做的积极方面是它完全是非侵入性的,并且不需要更改您现有的代码。
尝试以可以轻松添加这种横切关注点的方式设计您的应用程序。例如,将单个用例的业务逻辑/行为放在一个类中,并用通用接口装饰它:
public interface IUseCaseHandler<TUseCase>
{
void Handle(TUseCase useCase);
}
用例的定义是一个简单的DTO(数据传输对象):
public class MoveCustomerUseCase
{
public int CustomerId { get; set; }
public Address Address { get; set; }
}
实现可能如下所示:
public class MoveCustomerUseCaseHandler
: IUseCaseHandler<MoveCustomerUseCase>
{
public void Handle(MoveCustomerUseCase useCase)
{
// todo: business logic
}
}
你在这里得到了什么?好吧,当所有用例处理程序都实现了接口时,您可以为所有处理程序IUseCaseHandler<T>
编写一个装饰器:
public class DurationMeasuringUseCaseHandlerDecorator<TUseCase>
: IUseCaseHandler<TUseCase>
{
private readonly IUseCaseHandler<TUseCase> decoratedInstance;
private readonly ILogger logger;
public DurationMeasuringUseCaseHandlerDecorator(
IUseCaseHandler<TUseCase> decoratedInstance,
ILogger logger)
{
this.decoratedInstance = decoratedInstance;
this.logger = logger;
}
public void Handle(TUseCase useCase)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
try
{
// call the real use case handler
this.decoratedInstance.Handle(useCase);
}
finally
{
this.logger.Log(typeof(TUseCase).Name +
" executed in " +
stopwatch.ElapsedMiliseconds + " ms.");
}
}
}
这似乎有很多代码用于一点点日志记录,不是吗?不,实际上不是。这是唯一一次您必须编写该代码,并且您不必更改任何用例处理程序来添加测量。您可以像这样包装所有处理程序:
// Composing the object graph
IUseCaseHandler<MoveCustomerUseCase> handler =
newMoveCustomerUseCaseHandler(
new MoveCustomerUseCaseHandler(),
new Logger());
// Using the object (somewhere else in the code)
handler.Handle(new MoveCustomerUseCase { CustomerId = id, Address = adr });
但是现在我们每次想要使用它时仍然必须将对象图连接在一起?这就是 IoC 容器发挥作用的地方。例如,使用Simple Injector,只需一行代码即可在系统中注册所有用例处理程序:
container.RegisterManyForOpenGeneric(typeof(IUseCaseHandler<>),
typeof(IUseCaseHandler<>).Assembly);
RegisterManyForOpenGeneric方法遍历程序集中的所有公共类型,并通过其封闭的泛型表示(例如 )注册实现接口的所有具体IUseCaseHandler<T>
类型IUseCaseHandler<MoveCustomerUseCase>
。
用装饰器包装所有处理程序只是另一种方法:
container.RegisterDecorator(typeof(IUseCaseHandler<>),
typeof(DurationMeasuringUseCaseHandlerDecorator<>));
使用此配置,我们可以请求container
for an IUseCaseHandler<MoveCustomerUseCase>
,它会以MoveCustomerUseCaseHandler
用 a 包裹的形式返回DurationMeasuringUseCaseHandlerDecorator<MoveCustomerUseCase>
:
var handler =
container.GetInstance<IUseCaseHandler<MoveCustomerUseCase>>();
handler.Handle(new MoveCustomerUseCase
{
CustomerId = id, Address = adr
});