Introduction to Interceptors

Interceptors are an experimental feature in preview mode with C# 12. An interceptor is a method that allows you to substitute a call to an interceptable method, with a call to itself at compile time. In this article, I present how to create and use an interceptor.

Setting up Interceptors in .NET 8

For demonstration purposes, I created a console application with .NET 8 and opened it using the Visual Studio 2022 Preview version. To use this feature, you also need to add the following configuration in the PropertyGroup in your csproj (and replace the DotNet8Examples with your application’s namespace).

<InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);DotNet8Examples</InterceptorsPreviewNamespaces>

Creating InterceptsLocation attribute

Then it would help if you created the InterceptsLocation attribute. This can be done in a separate file, or in the same file where you have the code that you want to intercept. Note that if you add in a separate file, the namespace must be System.Runtime.CompilerServices, and not the namespace of your application. Create the attribute like this:

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
    public sealed class InterceptsLocationAttribute : Attribute
    {
        public InterceptsLocationAttribute(string filePath, int line, int character)
        {
            // Constructor logic here
        }
    }
}

This attribute contains three parameters. I will explain how to use them a bit later in this article, but let me explain what are they.

Creating the Intercept Class

I also created a class with three methods that print a value in the console, to use as an example.

public class InterceptableExample
{
    // This method will not be intercepted;
    public void PrintValue1()
    {
        Console.WriteLine("Test 1");
    }
    // This method will be intercepted;
    public void PrintValue2()
    {
        Console.WriteLine("Test 2");
    }
    // This method will not be intercepted;
    public void PrintValue3()
    {
        Console.WriteLine("Test 3");
    }
}

On the Program.cs file, I create an instance of this class, and I create a call to each of these three methods. The output can be seen below.

var interceptableExample = new InterceptableExample();
interceptableExample.PrintValue1();
interceptableExample.PrintValue2();
interceptableExample.PrintValue3();
// Output:
Test 1
Test 2
Test 3

Now, let’s create the intercept class. This class must follow these rules.

For demonstration purposes, I created a class that contains a method that will print the message Interceptor is here.

public static class Interceptors
{
    [InterceptsLocation(
        filePath: @"C:\Users\Henrique.Domareski\projects\github\DotNet8Examples\DotNet8Examples\Program.cs",
        line: 117,
        character: 22)]
    public static void InterceptMethodPrintValue2(this InterceptableExample example)
    {
        Console.WriteLine("Interceptor is here!");
    }
}

In this example, the method PrintValue2 from the InterceptableExample class is going to be intercepted, and instead of executing the method PrintValue2, the InterceptMethodPrintValue2 method will be executed.

For that, the filePath must be the full path of the Program.cs file (which is the file on which this method is being called). You can get this information by right-clicking into the class you want to intercept and selecting the “Copy Full Path” option:

Copy full path

For the line parameter value, you need to inform the code line of the method you want to intercept. In this example, the method PrintValue2 will be intercepted, and the call to this method it’s on line 117.