Traits in C# Part 3 — Stacking traits

This is the third part of the Traits in C# series. For your convenience you can find other parts in the table of contents in Part 1 — Basic implementation with Fody

Last time we implemented method overriding using traits. Today we are going to handle base method calls.

Table of Contents

Test program

We will slightly modify our last test program. Here goes the updated code:

In ordinary C# we would like to call base methods using base.Print. However, we cannot do that since we don’t have methods in interfaces, they are introduced by Fody during compile time. So what can we do? We can add stub method which we will replace with Fody. Stub method looks like this:

We extend System.Object which means that we can call this method with any object we like. Instead of calling base.Print, we call this.Base(). In Fody’s weaver we will take care of replacing call to Base with call to concrete method. However, if we do not replace the call, nothing wrong happens — extension method will just do nothing, and JIT should remove the call anyway.

Let’s implement logic.

Weaver

In fact, not much changes. First, we need to find our method:

Next, we need to fix logic modifying classes:

We basically have three possibilities when modifying class:

  • We don’t touch the class — because it doesn’t implement any interesting interfaces — in this case we need to fix base calls anyway since upper classes might implement some interface
  • We only add extension methods — because method with matching name already exists — we need to fix matching method since it might contain calls to base methods, we also need to fix extension methods
  • We add everything (extension methods + virtual method) — in that case we only need to fix extension methods

OK, so what’s new? We add a dictionary for existing methods we need to fix. Before injecting a method, we look for matching method from base classes or take already injected method with the same name. We inject extension to inheritor (will come back to this in a second). After introducing all methods, we fix hierarchy in the following way:

We examine base classes, try to find matching base methods, and replace calls. Please notice, that if there is any call to base method in one of base classes, it means, that this class does not implement interesting interfaces, and its base call should be redirected to virtual method in one of base classes. We do this recursively in order to fix all methods. We might check methods multiple times, but we will modify call to a method once for every new injected implementation.

Finally, we need to inject extension methods in slightly different way:

And we are done.

Results

As usual, below are decompiled codes. Since only implementation were changed, we do not examine interfaces this time. Test program:

Extension classes:

Concrete classes:

As we can see, everything is correct.

Summary

We have implemented very basic logic for introducing traits to C# codebase. We are now ready to use this feature to stack implementations in pretty nice way. Of course, there is much more to do in order to use it in the production environment.

First, we onlly support void methods without parameters. Handling different methods is not difficult, but it takes time. We could use lambdas to have compile time support, but verifying lambdas in Fody takes even more time.

Next, we only support public virtual methods. It is not difficult to modify behaviour using attributes, but once again — it takes time.

Third, we don’t report errors or check existing code — we blindly substitute methods without verifying whether everything makes sense.

These are of course cases which we should take care before deploying this code to production. But since we do it only for fun, we perform little to no error checking.

You can find whole code at Github