次の方法で共有


Reflection with dynamic

This past summer the Visual C# IDE team was fortunate enough to have Paul van Brenk intern with us. Paul is a great guy and an experienced .NET developer from Rotterdam, Netherlands, who is interested in Azure, cloud computing and many other things. He also happens to be one of the contributors for dasBlog, which I think is seriously cool.

Paul and I were sharing an office during his internship and I was the “coach”, i.e. the guy who gets to answer all intern’s questions and helps figure things out :)

Anyway, once we were talking about how to simplify the mess of calling private members using reflection and dynamic came into conversation. So Paul went ahead and wrote some C# 4.0 code that greatly simplifies calling private members using dynamic. Check out his blog: https://www.paulvanbrenk.com/blog/2009/05/30/UsingDynamicForEvil.aspx

There are several things I’d like to shed more light on.

Dynamic usage scenarios

It’s a common misconception that dynamic can only be used for narrow interop scenarios. People came up with ways to use dynamic in a lot more scenarios.

A common theme emerges from such usages: delay the name resolution (binding) to runtime. Instead of hardcoding the symbols known to compiler, we just specify source strings that have to be used at a later stage at runtime for lookup.

Dynamic gives you nicer syntax to express member calls – you express them in your code using usual C# syntax (but without type checking), and the compiler does the heavy lifting of “quoting” the code down to the runtime. If you inspect the generated code, you will see that the compiler is taking your neat member call syntax and generating a complex call site data structure that contains metainformation about the call.

How the language design team designed the dynamic feature

From previous versions of C# you will notice that the language design team is clever when adding new features. They don’t just hardcode a feature into the language, they define an interface and implement the feature against that interface. Not literally, mind you. On the contrary, they rather use duck-typing in most cases, but still there is a contract defined somewhere and the feature works against that contract. Let me give a couple of examples:

  • foreach – they could have added foreach just to arrays. Instead, they “designed against an interface” and said: foreach will work against any IEnumerable implementation. Moreover, the contract of the feature is actually broader and is specified using duck-typing and not an explicit interface. foreach actually works against anything that has a GetEnumerator method, it does not have to implement IEnumerable. The extensibility here is that you can plug-in your own enumerator whatever it is and foreach will work just fine with it.
  • LINQ – again, the language design team said: query syntax will just translate into a well-defined set of methods (Select, Where, etc.). The extensibility here is that you can define your own Select, Where, etc. and LINQ will rewrite the query expressions against your own methods – the queries will continue to just work with your own implementation of the query pattern.
  • dynamic – and here, in C# 4.0, they didn’t just add dynamic. They defined an abstraction layer (an interface) that allows you to plug in your own implementation and the feature (dynamic) will work fine with it.

In other words, they encapsulated an object with runtime callable members into an interface (IDynamicMetaObjectProvider) and provided default base implementation (DynamicObject). What this gives you is the ability to plug in your own implementation and dynamic will work against it.

Effectively, this means that you can intercept member calls on any object, if these calls are dynamic. The secret of “abusing” dynamic is to use member call syntax for stuff other than calling a method. By redefining DynamicObject members you handle member calls in any way you want (usually some sort of lookup by string) – dictionary lookup, XML element and attribute lookup, reflection MemberInfo lookup, and so on.

Now whether this all is good or bad is a totally separate discussion.