I could not have received a clearer answer from Anders Hejlsberg after asking him, during PDC 2010, if there was any chance of getting AOP support in the next version of C#.

“No.”

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”.

Why?

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 some solution 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.

Conclusion

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#.

Happy PostSharping!

-gael

Comments (15) -

Robert A. McCarter
Robert A. McCarter
11/12/2010 6:29:43 PM #

Beautifully written Gael, a wonderful response.

I especially like your response about Dependency Injection.  I love Dependency Injection as a design pattern, but shy away from IoC tools for lots of reasons, this being one of them.  I would MUCH rather use a proper, well designed and fully-capable AOP engine (like PostSharp) than an IoC to implement AOP.

Thanks again Gael for a wonderful tool - looks like you've got a good business model for years to come!  :-)

Robert

Thomas Darimont
Thomas Darimont
11/12/2010 6:40:32 PM #

Hi Gael,

I totally agree with you! Keep on PostSharping :)

BTW the link [WU1] points to your local filesystem... ;-)

Best regards,
Thomas

Axel
Axel
11/12/2010 8:48:06 PM #

Never write in anger. I personally would think twice before calling anyone of Ander's caliber a misinformed guy that stopped at Hello World... just my opinion of course...

Gael Fraiteur
Gael Fraiteur
11/12/2010 9:02:44 PM #

Two weeks after the facts, there's certainly no anger and even no excitement. Maximally some guerilla writing ;).

I do think that Anders is misinformed about AOP; he could not have said was he said otherwise. That said, I perfectly agree with the ideas and priorities of the C# team.

chris
chris
11/13/2010 3:59:35 PM #

I completly agree with Anders Hejlsberge.

Why?
Yes it helps you with development, Gael mentioned some interesting things. We for ex. also used also postsharp for transaction handling in C#2.0. There is definitley value in that. But since c# 3.0 for ex. we do things different like SpanTransaction.Context(a => a.DoSomedatabaseAction()).

The scenarios Gael mentions do also not show that compile time aspects are a good solution because in most cases the real reason is for AOP is crappy libraries.

Data Binding (INotifyPropertyChanged)
-> if wpf/silverlight/winforms ... databinding would be more flexible, you wouldn't even think about AOP

Data Validation
-> if your application is that simple that it is enough to validate data on entity level, it's definitly nice

Thread switching (marshaling from/to background or GUI threads)
-> in my opinion some syntactic sugar but I prefer to write SomeThreadingAbstraction(DoInBackground, NotifyWhenFinished), see also c#5.0

Thread synchronization (locking)
-> I don't know any scenario where i have such a simple scenario I could generalise and reuse with aspects. If I need some syncronised access i always expose functions like this, SyncronisedAccess(dict => MutateDictionary(dict))

Security (authorization)
-> We currently also use postsharp in this scenario. But it is a compromise, we are currently switching to self build runtime aspects with castle dynamic proxy. With postsharp we can't mock aspects, since it can only use knowledge which is available at compiletime it's not bendable enough to do security things using tdd.

Transaction boundaries
-> Again upper things, not necessary since better api's for. ex in c#5.0 or do your own helper functions.

Exception handling
-> Used it for that but since c#3.0 no need for that, HandleServiceCall(Myservice.Call(), handleSuccess, handleError)

Sanity checks (detection of memory leaks and deadlocks)
-> For tracing and debugging AOP really valueable, also Anders sees value in that. But it is an edge case.

Transacted objects (objects supporting commit/rollback)
-> I have better control over that using a proxy, maybe i need an interface more for that, but it is worth the additional testability.

Persistent objects (whether to database, XML config file, registry, app.config, …)
-> can't comment on that, but don't see a value in using AOP for that.

The main problem I have with Compile-Time-AOP is that I can't test it and "craft" it using tdd. I do most configuration at runtime because I have the knowledge only at runtime, this can be wcf bindings or in my case also Aspects on castle dynamic proxys.
Second reason i don't like compile time aop is it that people are compensating other code smell with it. If I need AOP to deal better with WPF-Databing then the solution would be that WPF should become better Databindable. If I need AOP to do better threading maybe you should use f#-agents or wait for c#5.0

That said, there is definitly value in compile-time-aop. But most times you are compensating other weaknesses or just don't understood the functional properties of c# or are using the wrong language.

Gael Fraiteur
Gael Fraiteur
11/13/2010 5:10:29 PM #

Chris,

Thank you for your extensive comment.

I notice most non-AOP workarounds you mention are based on functional programming. There's a lot you can address with anonymous methods. However, you have to touch each method where you need the effect. AOP provides what's called "quantification" in the jargon, i.e. you can apply the same aspect to thousands of fields or methods in a single declaration. Quantification is the single most important characteristic of AOP.

Elsewhere in your argumentation, you mention that you would better wait for a better WPF or C#. I'm ok with that, this is your personal judgement of the cost/benefit ratio of waiting, but (1) some persons can wait for C# v7 or v8 and (2) who knows what's your hoping for will ever be implemented?

My experience is that AOP customers have been able to factor out tens of thousands of lines of code.

Cristian
Cristian
11/17/2010 3:54:52 PM #

> AOP provides what's called "quantification" in the jargon, i.e. you can apply the same aspect to thousands of fields or methods in a single declaration. Quantification is the single most important characteristic of AOP.

There is a rather straightforward relationship between functional programming and AOP. In a very simple example, given a simple function and a logging annotation/wrapper:

let f x = x + 1
val f : int -> int

let wrapper f = fun x -> log x; f x
val wrapper : (a -> b) -> (a -> b)

then

let wrapped_f = wrapper f
val wrapped_f : int -> int

wrapped_f is the original f, plus whatever behavior wrapper implements around f. Once wrapper is abstracted via a Wrapper interface, all one needs to do is to provide the right Wrapper when constructing wrapped_f. As it can be seen from the example, wrapping one method is done via a very simple/terse incantation.

Using this in practice requires correctly wrapping each of the f definitions. You argue that AOP does the "quantification" in a single line of code for 1000 fields/methods, but that's not complete. AOP does "quantification" in a single line of code *plus* something that is allows it to identify the 1000 fields/methods that should have their behavior wrapped. For instance, some ambient naming convention or, gasp, explicit annotation, which allows the AOP dispatcher to separate the fields/methods that need be wrapped from the fields/methods that need not.

The choice is between:
- Ambient naming conventions to allow the AOP dispatcher to chose which fields/methods to be annotated with what properties.
- Explicit annotations of the fields/methods using special AOP syntax.
- Explicit declarations of the fields/methods to be wrapped in a special AOP module.
- Using the core language to add the wrappers.

Just as with IoC frameworks, the value of an AOP framework is unclear over using the core (functional) language.

Gael Fraiteur
Gael Fraiteur
11/17/2010 5:38:38 PM #

Quantification can be done in different ways. I'm not a big fan of naming conventions. It breaks too easily.

You can use apply aspects as annotations directly to the joinpoints. This is not a good example of quantification, but even this helps achieving a better separation of concerns, better abstraction and better readability (especially if many mangled aspects must be implemented manually).

You can use plain old annotations and make the pointcut understand them (every framework can do that afaik).

Aspect inheritance is very powerful. For instance, you apply the aspect on the base entity class, and ask all public instance properties to raise the PropertyChanged event. You can implement an opt-out mechanism using standard annotations. The same if you want transacted objects or undo/redo support. This is especially useful for large object models.

Then you can add aspects through an XML file or a user interface, which PostSharp does not support out of the box currently.

Gael Fraiteur
Gael Fraiteur
11/13/2010 5:20:34 PM #

Regarding testing:

you can test an aspect A by applying this aspect to a set of methods f1...fn and test that the combination of the aspect and the methods (i.e. A(f1),...,A(f2)) have the expected effect.

You can also unit-test that aspects give expected build-time errors; I built a small testing framework for internal testing of PostSharp. Every test is a standalone console application and the "test framework" is just a build script.

Steve Degosserie
Steve Degosserie
11/14/2010 5:50:35 AM #

I would kinda agree with Axel ... there's a lot of design considerations going into a language used by millions of developers. I would argue that C# evolution will probably go more into the dynamic programming direction than AOP. The most common AOP needs are covered by Dynamic Proxies & Dynamic Programming ... going further than than, I'm not sure that leveraging advanced AOP-capabilities is a good thing, at the very least because it introduces a risk in terms of code maintenance & knowledge transfer (not every developer is able to grasp that kind of things). You probably provide a few good arguments in favor of AOP, too bad that you mention PostSharp so many times. Also, it seems to me that your understanding of Dependency Injection, and more particularly of Inversion of Control, is not thorough. IoC is a design technique, DI & AOP features are just nice to have that comes for free with it when using a IoC container.

Gael Fraiteur
Gael Fraiteur
11/15/2010 1:14:30 PM #

Steve,

Thank you for your comment.

Dynamic programming and Inversion of Control solve a different problem than aspect-oriented programming. Dynamic proxies per se are not a solution, there are just an implementation detail. I find the fact that NHibernate relies on dynamic proxies is suspect, for instance.

Regarding knowledge transfer:

Probably not every developer is able to understand the details of AOP, but the average developer will not write aspects. They will use them. Thanks to aspects, the requirements on the technical skillsets of line-of-business or GUI developers actually decreases. This is one of the benefits of a better separation of concerns.

Regarding too many mentions of PostSharp:

You're on the PostSharp blog, isn't it? PostSharp is the only fully-fledged AOP framework for .NET. There are also frameworks that do a subset of AOP as a subset of their functionalities. The situation in Java is different: both AspectJ and Spring AOP are advanced and mainstream. The situation there is very different because Java is (at least, was) multi-vendor. The Eclipse Java compiler is extensible so it much easier to implement language extensions.

Dmitri
Dmitri
11/15/2010 8:29:47 PM #

I agree with Anders too. I think it's hard to view AOP as a separate programming discipline, and it's very hard to see the viability of a commercial tool that knowingly complicates your code base. I can still remember the times of PostSharp 1.5 where I had an unreadable monster aspect for INotifyPropertyChanged and I also had to write fairly obscure code like double-casting an object to an interface, e.g., var obj = (INotifyPropertyChanged)(object)myObj; That's just wrong, that is.

Don't get me wrong now - I like the idea, but I think the implementation fails. Take a look at Boo - much cleaner, much more powerful. If C#5 supports this sort of metaprogramming (and I hope it will, but we can't know for sure), PostSharp won't be relevant simply because compile-time constructs will be done 'naturally', without resorting to an IL rewriter.

Also, I do think your naming of both this post and the video is a bit over the top - too hysterical for my tastes. I realize that your business model is being threatened here, but I personally am banking on the fact that C#5 makes PostSharp largely irrelevant. Then again, Boo makes it irrelevant today, the only problem being that it's a non-mainstream, non-Microsoft language.

Gael Fraiteur
Gael Fraiteur
11/15/2010 8:45:04 PM #

I can't agree that C# 5.0 does not make PostSharp irrelevant. It makes a single minor use case less relevant and even then increases the needs for other kinds of aspects. Boo does not make PostSharp irrelevant, because Boo itself is irrelevant for commercial development. AspectJ does not make PostSharp irrelevant.

The code for NotifyPropertyChanged in PostSharp 2.0 is much cleaner. You still need a post-compile cast operator (Post.Cast<INotifyPropertyChanged>(obj)), but these are the things you can improve if you get some support from the compiler. You don't have to write special casts with AspectJ because the Java compiler is extensible.

The commercial viability of AOP has been proven by SpringSource. There's no reason that what benefits to Java developers would not be useful to C# ones. The commercial viability in .NET then depends on the control exerted by Microsoft on its market -- by locking the compiler and controlling thoughts in different ways.

Dmitri
Dmitri
11/15/2010 9:10:51 PM #

Gael,

I think I could argue the counterpoint that Boo is, in fact, relevant for commercial development because people like Ayende (and myself, in times past) have used it on shippable product. The point I was trying to make is not one suggesting that Boo is a suitable AOP alternative, but rather wanting to explain that a language solution far outweighs a library+IL rewriter solution.

As for INPC, it's a moot point really - the idea of dragging in some Post.Cast<> shenanigan into my code is completely abhorrent. I do not mind taking a dependence on libraries where they provide run-time functionality. But unfortunately, PostSharp not only provides compile-time functionality but also binds itself so that it needs to be shipped with the library. I for one cannot see any tangible reason for this to be the case.

As to the commercial viability of AOP, all I'm arguing is that AOP is directly replaceable by a metaprogramming system. I mean, yes, you would lose the ability to overwrite other people's IL with your pre/post code, but I think you'll find that most of the motivation for AOP is to embellish your own code. And if we can agree that this is largely the case, I think it's very difficult to argue with the idea that a fully-fledged metaprogramming system handles not only handles AOP concerns very well (take a look at INPC implementation in Boo, for example), but also handles a much greater variety of concerns and does it much more efficiently. One of the reasons for this is that the quote and splice operators are more succinct and understandable than having to do the same with a library API.

Gael Fraiteur
Gael Fraiteur
11/15/2010 9:21:50 PM #

The reason why Boo is irrelevant today is that it is non-mainstream, as you mentioned. I'm not doing a technical judgement.

AOP is not bound to MSIL rewriting. AspectJ is implemented in different ways, some are bytecode rewriters, some are compiler extensions.

We agree on the need for metaprogramming. AOP is a specific way to express metaprogramming in a disciplined matter. People use AOP to improve their own code or the code of their team. The fact that PostSharp uses MSIL rewriting is that it is the more rational way to implement the technology in the .NET environment. Things could change with an open compiler, but this possibility is just speculation.

Therefore, I don't think we fundamentally disagree; we all want more metaprogramming, and we want it to be easy. It's just that many people find it easier to adopt an add-on tool that works well enough with C# (PostSharp) than a brand new language that solves all contradictions but that commercial companies are not willing to use (because it is non mainstream).

Pingbacks and trackbacks (8)+

Comments are closed