Avoiding Repetitive Code With Metalama

Gael Fraiteur
Gael Fraiteur
Avoiding Repetitive Code With Metalama
Table of Contents
Table of Contents

If you write code in C#, you're probably also writing repetitive code against your will. Think of caching, security, precondition checking, exception handling, design patterns like Builder or Memento, deep cloning, INotifyPropertyChanged...

In this article, I'll introduce Metalama, a new meta-programming framework my team and I built to automate code generation and validation in C#. Metalama is partially open-source. Its free edition includes everything covered in this article.

Why do we have repetitive code in C#?

Boilerplate code happens because we think at a higher level of abstraction than we can write code in C#. As human beings we are able to think "all public methods of the Daisy.Barn namespace must be logged, have accurate parameter checking, and be transactional". However, the C# language does not allow us to translate this into source code. Therefore, it must be done manually.

Several technologies have been designed to fill this abstraction gap. At one end of the spectrum, we have very academic aspect-oriented programming (AOP) with AspectJ, its canonical implementation for Java. On a much lower level, we have tools like Fody, which is already mentioned in this blog and allows you to manipulate individual MSIL instructions (thus working at an even lower level of abstraction than the one of C#). Twenty years ago, I founded PostSharp, a pragmatic implementation of aspect-oriented concepts for .NET.

Introducing Metalama

I've been working on a new Metalama project Metalama in the last few years. Like Fody and PostSharp, it aims to solve the challenge of repetitive code in C#. But unlike them, it is based on the Roslyn compiler, the new open-source C# compiler.

The idea behind Metalama is that you write custom attributes called aspects, which work as code templates. Here is our Hello World example: a logging aspect.

First, let's add a package to the project:

<PackageReference Include="Metalama.Framework" />

We can now start with the aspect code.

using Metalama.Framework.Aspects;

public class LogAttribute : OverrideMethodAspect
{
  public override dynamic? OverrideMethod()
  {
    Console.WriteLine( "Entering {meta.Target.Method}." );
    try
    {
       return meta.Proceed();
    }
    finally
    {
       Console.WriteLine( "Leaving {meta.Target.Method}." );
    }
  }
}

This code is not your normal C#; it is a template. Anything right of meta is a meta expression or statement and gets evaluated at build time when the template is expanded. So, meta.Target.Method[.ToString()] will be evaluated as the signature of the method to which the aspect is applied.

You can then use the aspect on any method, like this:

[Log]
void Bleat() => Console.WriteLine( "Meee!" );

Metalama transforms the code during compilation into this:

void Bleat()
{
  Console.WriteLine( "Entering Program.Bleat()" );
  try
  {
    Console.WriteLine( "Mee!" );
  }
  finally
  {
  Console.WriteLine( "Leaving Program.Bleat()" );
  }
}

Llamas, by the way, don't bleat but emit a sound typically referred to as a "hum". And if you ask, llamas are not related to goats but camels. I'm sure we can still be friends, however.

Mais revenons Γ  nous moutons...

The real benefit of this approach is that you can drastically reduce repetitive code. If you want to change the code generation pattern (for instance, include parameter values in your log), the aspect is the only thing you must change. You get the idea!

I invite you to read this series on implementing logging with Metalama to explore this topic further.

Since Metalama works at C# level and not at MSIL level, it’s possible to preview or even debug the code generated by Metalama.

Ready-made aspects

While Metalama makes it reasonably straightforward to build simple aspects, building more complex ones requires, of course, more effort.

If you're concerned about learning a new framework, good news is, you don't need to write your own aspects. You can find open-source and professionally supported aspects on Metalama Marketplace, including code contracts, caching, observability (INotifyPropertyChanged), WPF commands and dependency properties, and much more.

There are also numerous commented examples for the most common use cases.

Example: INotifyPropertyChanged

Let's examine Metalama's [Observable] aspect to give you a taste of what it's like to use Metalama in a project.

Implementing INotifyPropertyChanged is often dull but not always trivial. Here are a few scenarios supported by the [Observable] aspect:

  • Automatic properties;
  • Explicitly-implemented properties whose getter references one or many fields, other properties, non-virtual instance methods;
  • Child objects, i.e., properties whose getter references properties of another object, referred to as a child object, stored in a field or an automatic property of the current type (if this child object is itself observable);
  • Class inheritance.

To illustrate the problem, think of how you would implement INotifyPropertyChanged by hand.

[Observable]
public class Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}

[Observable]
public class PersonViewModel
{
    public Person Person { get; set; }

    public PersonViewModel( Person model )
    {
        this.Person = model;
    }

    public string? FirstName => this.Person.FirstName;
    public string? LastName => this.Person.LastName;
}

public class PersonViewModelWithFullName : PersonViewModel
{
    public PersonViewModelWithFullName( Person model ) : base( model ) { }

    public string FullName => 
         $"{this.FirstName} {this.LastName}, {this.Person.Title}";
}

All you need with Metalama is the [Observable] aspect. If you're curious, see how much code the aspect generates for you!

For details, see the reference documentation of this aspect. And shht, it's open-source.

Wrapping up

If you have repetitive code, Metalama can most likely automate it. Whether you prefer ready-made, battle-tested solutions or build your own and master every single detail, Metalama offers it all.

You can learn more about Metalama thanks to the video tutorials and commented examples. Our development team looks forward to your feedback and questions on our Slack community workspace.

Happy grazing!



Join the conversation.

Great! Check your inbox and click the link
Great! Next, complete checkout for full access to Goat Review
Welcome back! You've successfully signed in
You've successfully subscribed to Goat Review
Success! Your account is fully activated, you now have access to all content
Success! Your billing info has been updated
Your billing was not updated