先週、もう一度パフォーマンス測定を行いましたが、同じユーティリティコードを何度も記述しない方がいいと思いました(手元にプロファイラもありませんでした)。 そして、呼び出しを委任し、パフォーマンスを測定するタスクを実行し、アルゴリズムに集中できるようにする、調査中のクラスのプロキシクラスが必要です。 しかし、私はそれを手動で書いたり、サードパーティのツールの助けに頼ったりしたくありませんでした。 したがって、最小限のコードで各クラスのコントラクトを維持する方法の質問に答え、さらにパフォーマンス測定コードを挿入する必要がありました。
そして、動的データ型が契約を「サポート」するのに役立つという考えが浮かびました。 また、クラスのエンドメンバーを呼び出すときに、ユーティリティコードを挿入できます。 すぐに言ってやった! 次のようなドメインモデルがあるとします。
using System;
using System.Threading;
namespace DynamicWrapperTest
{
public class Payment
{
public Guid Id { get ; set ; }
public Guid UserId { get ; set ; }
public Guid OperationId { get ; set ; }
public decimal Amount { get ; set ; }
public string Description { get ; set ; }
}
public class PaymentService
{
private readonly Random rand = new Random (Environment.TickCount);
public void MakePayment(Payment payment)
{
Thread.Sleep( TimeSpan .FromSeconds(rand.Next(5)));
}
}
}
* This source code was highlighted with Source Code Highlighter .
ここで、基本的な動的オブジェクトを実装します(階層の特別な必要はありませんでしたが、習慣から、すぐに拡張の基礎を築きました)。
using System;
using System.Dynamic;
namespace DynamicWrapperTest
{
public class DynamicWrapper : DynamicObject
{
private readonly object source;
public DynamicWrapper( object source)
{
this .source = source;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object [] args, out object result)
{
var methodInfo = source.GetType().GetMethod(binder.Name);
if (methodInfo != null )
{
Func< object , object [], object > func = (s, a) => methodInfo.Invoke(s, a);
result = MethodCall(func, source, args);
return true ;
}
result = null ;
return false ;
}
protected virtual object MethodCall(Func< object , object [], object > func, object src, object [] args)
{
return func(src, args);
}
}
}
* This source code was highlighted with Source Code Highlighter .
次は、メソッド呼び出しとパフォーマンス測定を行う最終オブジェクトです。 ご覧のとおり、これはWrapperパターンの実装であり、プロキシパターンではありません。
using System;
using System.Diagnostics;
using System.IO;
namespace DynamicWrapperTest
{
public class ProfilerWrapper : DynamicWrapper
{
private readonly int iterationCount;
private readonly TextWriter output;
private readonly Stopwatch stopwatch = new Stopwatch();
public ProfilerWrapper( object source, int iterationCount, TextWriter output)
: base (source)
{
this .iterationCount = iterationCount;
this .output = output;
}
protected override object MethodCall(Func< object , object [], object > func, object src, object [] args)
{
object result = null ;
for ( var i = 0; i < iterationCount; i++)
{
stopwatch.Restart();
result = base .MethodCall(func, src, args);
stopwatch.Stop();
output.WriteLine( "Step #{0} : Method call in {1} ms" , i + 1, stopwatch.Elapsed.TotalMilliseconds);
}
return result;
}
}
}
* This source code was highlighted with Source Code Highlighter .
そしてテストコード:
using System;
namespace DynamicWrapperTest
{
class Program
{
static void Main( string [] args)
{
var payment = new Payment
{
Id = Guid .NewGuid(),
UserId = Guid .NewGuid(),
OperationId = Guid .NewGuid(),
Amount = 500m,
Description = "Feed developers plz!!!"
};
var paymentService = new PaymentService();
dynamic dynamicWrapper = new ProfilerWrapper(paymentService, 10, Console .Out);
dynamicWrapper.MakePayment(payment);
Console .ReadKey();
}
}
}
* This source code was highlighted with Source Code Highlighter .
残りの時間は、他のどこで役立つかを考えて過ごしました。 翌朝仕事に着くと、 ダニエル・カズリーノからの同様のアプローチがRSSフィードで見られました。 彼とDavid Ebboは、 このアプローチを使用して、リフレクションとプライベートメンバーへのアクセスを簡素化します。 彼らの解決策では、メンバー検索コードはより普遍的で正確ですので、勉強することを強くお勧めします。 また、同じ賢いアイデアが一度に数人の人の頭に浮かぶ可能性があるという声明をもう一度確認できたことを嬉しく思いました。