Saturday 22 January 2011

Simulate Anonymous Classes in C#

I'm starting to learn Groovy. I've been more on the .Net side all these years cause I think Java (the language) sucks because of its lack of "freak features" when compared to C#: delegates and events, properties, lambdas, Iterator blocks, object literals, unsafe, PInvoke, dynamic... but Java the platform is at least as powerful as .Net, so using a modern language like groovy seems like a good option for me to be able to leverage all the beautiful Java frameworks out there...<

What Groovyists call Closures (they use it as a syntactic concept instead of semantic and admit that the name is confusing, as sometimes these anonymous methods enclose external variables acting as real closures, other times, there's no need for this enclosing, so calling them closures is not fully accurate), are pretty similar to .Net delegates. In both worlds functions are not objects, and the framework and compiler work together to create delegate or Closure objects to wrap functions.
A really beautiful feature is coercing Closures or Maps to implement interfaces. This nice technique makes Java anonymous classes unnecessary, but at the same time reminded me of this Java feature and made me think of it in C# terms.

In spite of all its power, C# 4 does not have anonymous classes. It's strange, cause given all the crazy things the C# compiler does (creating classes for closures, iterator blocks...) adding support for anonymous classes seems pretty easy.
I guess the main reason is that at first sight they don't seem much useful. In Java Anonymous classes are mainly used for the events-listeners thing, in C# the very convenient delegate (named and anonymous) approach is used for event handling, so this use case is over. However, as you can read in this interesting discussion, implementing interfaces with anonymous classes can be pretty useful for testing(Groovyists know it well too), avoiding us to pollute our namespace with unnecessary class names.

So, how can we implement something similar in C#?

  • First we want to create an object with x properties and methods without declaring a class for it (going the JavaScript way here :-)

  • Second, we'll need to make that normal object implement an interface (compatible with the properties and methods in the object). Seems clear that Dynamic Proxies are the logical choice for this (yes, I have to admit here that it's rather odd that Java (the platform) has included dynamic proxies since a long while and in .Net we still have to depend on third party implementations).



So, for the first part, the object creation, what can we do?
Anonymous types:

var obj = new {
Name : "Xurde",
Age : 15
};

Anonymous types are pretty limited. We can't simulate methods by adding delegates to them, they're just a "data bag"... so they're of no use for this.

dynamic (keyword) and ExpandoObject. Right, this .Net-C# addition is the way to go. We can add both data and code (using delegates) to these nice objects.
I did some googling regarding "make ExpandoObject implement interface" and found some good posts. This brave guy dared to go down the path of implementing their own Proxy generation code. It's not that System.Reflection.Emit scares me... but I preferred the lazy path of using some existing DynamicProxy solution, and Castle DynamicProxy is the only one I'm slightly familiar with.

So, with this source here, given an interface like this:

public interface IPrintable
{
string Print();
string Print2(string st);
string Name
{
get;
set;
}
}

we can write code like this:

dynamic ob1 = new ExpandoObject();
ob1.Name = "Xana";
//notice that we're using ob1 instead of "this", turning the anonymous delegate into a closure
ob1.Print = (Func<string>)(() => "print: " + ob1.Name);
ob1.Print2 = (Func<string, string>)((st) => "print: " + st + ", " + ob1.Name);
IPrintable printableProxy = ExpandoObjectProxyGenerator.GetProxy<IPrintable>(ob1);
//method invocation
Console.WriteLine(printableProxy.Print());
//get property
Console.WriteLine(printableProxy.Name);

//set property
printableProxy.Name = "Xuan";
Console.WriteLine(printableProxy.Name);


All it comes down to using an Interface Proxy without Target, and implementing an IInterceptor like this:


public void Intercept(IInvocation invocation)
{
Console.WriteLine(invocation.Method.Name);
if (invocation.Method.Name.StartsWith("get_"))
//reading a property
{
string propName = invocation.Method.Name.Split(new char[]{'_'})[1];
invocation.ReturnValue = ((IDictionary<string, object>)(this.target))[propName];
}
else if (invocation.Method.Name.StartsWith("set_"))
//setting a property
{
string propName = invocation.Method.Name.Split(new char[]{'_'})[1];
((IDictionary<string, object>)(this.target))[propName] = invocation.Arguments[0];
}
else
//method invocation
invocation.ReturnValue = ((Delegate)(((IDictionary<string, object>)(this.target))[invocation.Method.Name])).DynamicInvoke(invocation.Arguments);


The tricky part is that we have to differentiate between access to a method of the interface and access to properties of the interface. For properties access (get or set) Castle's Invocation.Method will point to the get_[propertyName], set_[propertyName] that the C# compiler automatically creates under the covers for properties.

While working on this, I learnt a lot of interesting things about ExpandoObjects (I have to admit I'd never used them before) and this article pretty helped me. They're a really interesting way to provide us with the expansion capabilities of Python or JavaScript objects, but they have a flaw when compared to these objects. Doing something so natural in JavaScript like accessing a method or property based on a string name, that is, something like myPerson["age"], requires some more verbose code in C#, something like:
((IDictionary<string,object>)myPerson)["Age"];
The ExpandoObject implements IDictionary<string, object>, but for some reason (unknown to me) it's using explicit interface implementation instead of implicit, hence the need for that casting. This is event more verbose for method invocations, as we'll need to add one more cast, this time to a compatible delegate type:
((Func<string>)((IDictionary<string,object>)myPerson)["Print"])();

No comments:

Post a Comment