⬆️ ⬇️

Measure performance with DynamicObject

With dynamic data types there is a situation similar to AOP . Namely, useful examples of this technique can be counted on fingers, and they are worthy of collecting (one of the collections on AOP is collected here ). Today, I hope we will add a couple more of these examples.





Last week, doing once again the performance measurement, I thought it would be good not to write the same service code several times (the profiler was not at hand either). And I would like to have for the studied classes of proxy classes that would perform the tasks of the delegation of calls and performance measurement, and would allow to focus on the algorithm. But I did not want to write it manually or resort to the help of third-party tools. Consequently, it was necessary to answer the question of how to support the contract of each class with the minimum amount of code, and insert the performance measurement code.

And here the thought came to mind that a dynamic data type would help to “support” the contract. And while calling the end member of the class, you can insert any service code. No sooner said than done! Suppose we have such a domain model:

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 .


Now we are implementing a basic dynamic object (there was no special need in the hierarchy, but, out of habit, it immediately laid the foundation for expansion):

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 .


Next is our final object, which takes a call to the method and measure performance. As you can see, this is the implementation of the Wrapper pattern, not the Proxy pattern:

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 .


And test code:

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 .


I spent the rest of the day wondering where else it might be useful. Coming to work the next morning, I saw in the rss-tape a similar approach from Daniel Cazzulino . He and David Ebbo use this approach to simplify reflection and access to private members. In their solution, the member search code is more versatile and accurate, so I strongly recommend that you study it. I was also pleased to confirm once again the statement that one and the same clever idea can occur to several people at once.


')

Source: https://habr.com/ru/post/120294/



All Articles