I was relieved. But then Anders started spreading myths about AOP, not only showing that he’s misinformed about the technology, but also that he’s blatantly ignoring the needs of a large portion of C# developers – including some Microsoft teams.
Anders, I am totally relieved that C# will not support AOP. I understand your reasons, as the architect of Microsoft’s flagship language, but please allow me to respectfully demystify the untruths you’re spreading.
Myth 1. “Aspect-oriented programming is interesting for debugging and instrumentation of code and is not a full-fledged programming discipline.”
Truth 1. Anders probably stopped at the “Hello, world” example.
Although code instrumentation is certainly an important use case of AOP – and the one you would see in every “getting started” documentation – the technology significantly simplifies the work of developers when it comes to implement any non-trivial, real-life application. Just to cite a few real-life scenarios where AOP really helps:
- Data Binding (INotifyPropertyChanged)
- Data Validation
- Thread switching (marshaling from/to background or GUI threads)
- Thread synchronization (locking)
- Security (authorization)
- Transaction boundaries
- Exception handling
- Sanity checks (detection of memory leaks and deadlocks)
- Transacted objects (objects supporting commit/rollback)
- Persistent objects (whether to database, XML config file, registry, app.config, …)
Myth 2. ”You can take an arbitrary body of code that you did not write and inject your code in the middle of it and start messing with invariants that programmer thought were true and probably aren’t any more.”
Truth 2. Mature aspect-oriented frameworks offer a disciplined approach to programming
To Ander’s defense, let me acknowledge that AspectJ has been used for the ugly.
You can find some terrific examples in scientific literature where people have proudly been using aspects to fix bugs in a third party library, thereby turning AOP into a patching technology.
Let me be very clear about this: patching and AOP are different things, even if the same tools can be used. These cases are pretty much limited to Java, because .NET makes it much more complex to change code you have not written yourself (because of strong names).
PostSharp only enhances the code you own. It is a build-time tool. Even if PostSharp didn’t not look at the source code, the code is typically present on the developer’s machine. If you did not write that piece of code yourself, it was probably written by your colleague. As an architect, there are actually many aspects you want to be applied to the code written by line-of-business developers. You don’t want them to care about exception handling or change notification, so you define that these features should be added to all methods or properties of all domain objects. It takes you 20 lines of code in the base class and PostSharp writes the remaining 20,000 lines of code in business objects. This is aspect-oriented programming. This is actually what you want.
PostSharp won’t let you inject code in the middle of nowhere. PostSharp uses a model named composition filters, developed by the University of Twente. This model represents a method call as an exchange of messages (request/response) between the caller and the called nodes. Aspects work as filters on these messages. They can alter them, they can choose not to pass them forward, but they cannot change the internal behavior of nodes. If you apply several filters to the same node, PostSharp will require you to specify dependencies. The aspect dependency framework is engineered to handle situations when one uses several aspects from vendors who don’t know about each other. PostSharp is robust—by design.
PostSharp makes you aware of the presence of an aspect. If your code has been modified by an aspect – even if the aspect is located in another project – Visual Studio will underline it in light blue. Move the mouse pointer to the method name, and you’ll see the list of all aspects applied there. This is a unique feature of PostSharp 2.0 Pro.
Ok, I admit it. You can add an aspect to the method Math.Sin so that it always returns 2.67. Even if you can’t break the encapsulation of a method, you can add aspects to private methods of a class, arguably breaking the encapsulation of the class. Yes, you can do some powerful stuff with a sharp tool, and you can get hurt if you don’t use it correctly. Anders knows it. C# does native pointers, doesn’t it? The solution is not to give pros toy tools, but to make the tool safe by design.
Did you ever wonder why developers move away from static languages to dynamic languages? Partly, because the strong discipline of C# prevents them from doing what they need in an effective way. Whether Anders likes it or not, developers need meta-programming. If we as language designers don’t provide them with a disciplined approach to meta-programming, developers will move away from any disciplined language.
If governments stop listening to their voters, people start voting with their feet. This is exactly what’s happening now with C# folks moving to Ruby. And this is the gap PostSharp is trying to bridge: Anders, we love the way C# makes us write well-structured, machine-verifiable, refactorable code, but SharpCrafters listens when people ask for meta-programming, and we provide a framework that lets them make code that’s still machine-verifiable, still refactorable, and even better structured and maintainable than before. And this framework is PostSharp 2.0.
Myth 3. “A lot of the same things become possible with dependency injection.”
Truth 3. Using dependency injection for the sake of AOP is like buying a car for the purpose of sleeping on the back seats.
Dependency injection exists for a single purpose: to provide a specific implementation to objects requiring an interface. Dependency injection makes it practical to follow the design principle “program to an interface, not to its implementation”.
All dependency injection frameworks now come with some AOP-like feature: when you require the implementation of an interface, you don’t receive the implementation itself, instead you receive a proxy (called DynamicProxy), and this proxy applies some aspects before invoking the final implementation. It looks like AOP, it smells like AOP, but it hardly compares to a fully-fledged AOP framework.
The first issue is that dynamic proxies only allow you to add aspects to the explicit boundaries of a service, the ones you chose to expose as an interface. You can’t add aspects to private or static methods, even if you wanted to. You can’t add aspects to fields. Even if you added aspects to public methods, the aspects methods won’t be invoked when you call them from the same object (the call would not go through the proxy).
The second issue is more dramatic. As you become addicted to some of the benefits of AOP, you start using dynamic proxies even where you don’t need dependency injection, altering the architecture of your code just because of the requirements of dynamic proxy. And this is wrong. AOP does not require you to change the architecture of your code.
Dependency injection is a great way to isolate components and services. If you are isolating your domain objects with dynamic proxies, you’re probably doing something wrong.
And have you noticed you can’t use dynamic proxies on WinForms or WPF objects? Surely you want AOP there too.
Myth 4. “[Aspect weaving happens] later through a bytecode writer, that’s sort of where I get off the bus.”
Truth 4. Anders is not on the Microsoft Code Coverage or Visual Studio Code Coverage bus.
Many people have an esthetic resentment against MSIL rewriting. When asked for a rational explanation, people usually answer that it’s “sort of dirty”.
MSIL is well documented, the CLR behaves almost perfectly according to its specification, so what’s wrong in producing verifiable MSIL instructions? What’s wrong in modifying the assembly before it’s actually being tested?
Many tools use MSIL rewriting, including Microsoft Code Contracts and Visual Studio Code Coverage. I’m happy that PostSharp drives this bus.
Myth 5. “We have better ways of addressing these scenarios.”
Truth 5. Anders has better ways because he owns C#, but we don’t. We can’t wait for C# 12 to have a solution to NotifyPropertyChanged.
I agree. C# 5.0 will be wonderful. The new keywords async and await solve the problem of asynchrony in a wonderful way. There’s no irony here, I’ve written my thoughts on it previously: the solution is better by an order of magnitude than what we can do with AOP.
PostSharp users, however, have had somesolution to this issue for four years. Not a perfect solution, but some solution. And most importantly: developers have been able to develop this solution themselves. They didn’t need to wait for the C# team, or for me – they had a toolkit that allowed them to write their own solution.
I fully understand that when the C# team picks up a problem it has – and succeeds – to find a magnificent ad-hoc solution. The point is: there are lots of issues where AOP can help, and if we had to wait for the language to come with an ad-hoc solution, we would probably wait forever.
What’s more, do we really need an ad-hoc solution every time? Wouldn’t it be better to abstract this set of issues and design a solution to the abstract problem? This solution is aspect-oriented programming.
Anders Hejlsberg fails to acknowledge the need for better support for meta-programming in C#, which is what AOP addresses. It’s not just about me and several thousand PostSharp users. Many teams in Microsoft itself have identified the same need. Windows Communication Framework (WCF) has its AOP framework (generalized into custom behaviors). ASP.NET MVC has its own (filters). Unity and PIAB have their own. Folks from Code Contracts would probably have appreciated the ability to hook the compiler. This need is largely being ignored.
There’s some speculation that C# 6.0 would “open the compiler”. From what I could hear from Anders Hejsberg and Mads Togersen, there’s no indication that the compiler would allow “extensions” to hook into the pipeline, much less modify its object model. Instead, we see the sign of fear of “someone messing with invariants” and of intractable cases to be handled by Microsoft Support. With regards to supportability, designing an extensible compiler is difficult, so I would not expect the first version “managed compiler” to support any extensibility. Therefore, within a horizon of 8-10 years (beyond which it’s difficult to predict anything anyway), I would not bet on an extensible compiler.
Therefore, PostSharp is there to stay. There’s a gap between the disciplined but inflexible programming model of C#, and the flexible but undisciplined model of dynamic languages. PostSharp bridges this gap, and PostSharp may well convince some folks to stay with C#.