Tags

, ,

It’s time for another adventure with Castle.DynamicProxy. One of the few things I really like in C++ is RAII – for example you can write code like this:

class DatabaseContext
{
public:
    virtual ~DatabaseContext()
    {
        CloseConnectionIfOpen();
    }

    void ExecuteNonQuery(string query)
    {
         OpenConnectionIfClosed();
         // and execute query...
    }

    // ...
}

// somewhere else
void DoSomething()
{
    DatabaseContext db;
    db.ExecuteNonQuery("DELETE * FROM tb_users");
}

ExecuteNonQuery opens connection to the database and DatabaseContext destructor takes care of closing it regardless if an exception occurred or not. Great! In C# you need more verbose using/IDisposable or try/finally and… you have a chance to forget about them.

But Castle.DynamicProxy comes to the rescue:

Initially this proxy was much simpler but I wanted to support also iterators. Methods returning IEnumerable are tricky. The compiler rewrites them to the state machine i.e. code:

IEnumerable<int> GetEnumerable()
{
    for (int i = 0; i < 10; i++)
        yield return i;
}

List<int> GetList()
{
    var iterator = GetEnumerable(); // for loop not reached yet!
    return iterator.ToList(); // iterator executed
}

Here yield return is only a promise: it will be executed later when the .ToList() is called. As a result proxying those methods is not enough in my situation. What I really need to emulate RAII is to catch the moment after execution of the iterator. But how to do that? Well, the compiler can handle using statements inside iterators:

IEnumerable<int> GetEnumerable()
{
    using (... something IDisposable ...)
        for (int i = 0; i < 10; i++)
            yield return i;
}

List<int> GetList()
{
    var iterator = GetEnumerable();
    return iterator.ToList(); // Dispose() called
}

That’s because it returns control to the iterator after leaving foreach loop (ToList method is actually a foreach loop). So the easiest way to do the same is to wrap GetEnumerable with another iterator i.e. effectively proxying resulting iterator object. That’s what I did.

Advertisements