danielwertheim

danielwertheim


notes from a passionate developer

Developer that lives by the mantra "code is meant to be shared".

Share


Tags


Disclaimer

This is a personal blog. The opinions expressed here represent my own and not those of my employer, nor current or previous. All content is published "as is", without warranty of any kind and I don't take any responsibility and can't be liable for any claims, damages or other liabilities that might be caused by the content.

C# - Building a dynamic method recorder using DynamicObject

Daniel WertheimDaniel Wertheim

Was at an OpenSpace event today in Stockholm. Was really great. Lots of inspiring discussions. Was in a discussion about Ruby/IronRuby and C# where there was this case where a method call recorder was built with a few lines in Ruby and that it wasn't possible to do it that easy in C#. Well, I kind of disagreed and took up my laptop and put together a few lines (a bit rough) since I just wanted to check if it was doable. The resulting code is below, and yes it is possible.

The case: Create an class that you can invoke methods on dynamically and then replay those calls on another class with the same API.

Note, that the code below isn't optimized but just a proof of concept

You could probably also remove the reflection and use a binder to invoke the method, but I don't know. As I said just a proof of concept.

Calling code

static void Main(string[] args)  
{
    //Record some calls
    dynamic dyn = new Recorder();
    dyn.Write("Daniel");
    dyn.WriteLine(" Wertheim");
    dyn.WriteLine(" building a");

    dyn.PlayBackOn(new Writer());
}

The recorder

public class Recorder : DynamicObject  
{
    private readonly Queue<KeyValuePair<string, object[]>> _calls
        = new Queue<KeyValuePair<string, object[]>>();

    public override bool TryInvokeMember(
        InvokeMemberBinder binder, object[] args, out object result)
    {
        _calls.Enqueue(new KeyValuePair<string, object[]>(binder.Name, args));
        result = null;
        return true;
    }

    public void PlayBackOn<T>(T item)
    {
        var t = typeof(T);

        while(_calls.Count > 0)
        {
            var kv = _calls.Dequeue();
            t.GetMethod(kv.Key).Invoke(item, kv.Value);   
        }
    }
}

The object being played

public class Writer  
{
    public void Write(string arg)
    {
        Console.Write(arg);
    }

    public void WriteLine(string arg)
    {
        Console.WriteLine(arg);
    }
}

As always, have fun.

//Daniel

Developer that lives by the mantra "code is meant to be shared".

Comments