winform .NET アプリの各メソッドの実行時間を測定する必要があります。出力はファイルです。
コードの開始時刻と終了時刻を各 .NET メソッドに配置する必要があるのではなく、構成ファイルを変更してオン/オフを切り替えることでこれを実行できるライブラリがあるかどうか疑問に思っています。
ありがとう。
コロンクス
この分野横断的な問題を簡単に追加できるように、アプリケーションを設計してみてください。たとえば、1 つのユース ケースのビジネス ロジック / 動作を 1 つのクラスに配置し、それを汎用インターフェイスで装飾します。
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を使用すると、1 行のコードですべてのユース ケース ハンドラをシステムに登録できます。
container.RegisterManyForOpenGeneric(typeof(IUseCaseHandler<>),
typeof(IUseCaseHandler<>).Assembly);
RegisterManyForOpenGenericメソッドは、アセンブリ内のすべてのパブリック型を反復処理し、IUseCaseHandler<T>
その閉じたジェネリック表現 ( などIUseCaseHandler<MoveCustomerUseCase>
) によって、インターフェイスを実装するすべての具象型を登録します。
すべてのハンドラーをデコレーターでラップすることは、別のワンライナーです。
container.RegisterDecorator(typeof(IUseCaseHandler<>),
typeof(DurationMeasuringUseCaseHandlerDecorator<>));
この構成では、container
forをリクエストでき、 でラップさIUseCaseHandler<MoveCustomerUseCase>
れたものとして返されます。MoveCustomerUseCaseHandler
DurationMeasuringUseCaseHandlerDecorator<MoveCustomerUseCase>
var handler =
container.GetInstance<IUseCaseHandler<MoveCustomerUseCase>>();
handler.Handle(new MoveCustomerUseCase
{
CustomerId = id, Address = adr
});
postsharpのような C# 用の AOP ライブラリを使用できます。
ポストシャープのドキュメントから変更された例:
/// <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()]
これの良い面は、完全に非侵襲的であり、既存のコードを変更する必要がないことです。