.NET 8 and C# 12 — Interceptors

Henrique Siebert Domareski
4 min readDec 1, 2023

--

Interceptors are an experimental feature, available 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.

For demonstration purposes, I created a console application with .NET 8, and I 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>

Then you need to create 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(string filePath, int line, int character) : Attribute
{
}
}

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:

  • The filePath is the path of the file you want to intercept.
  • The line is the code line you want to intercept.
  • The character is the character of your code that you want to intercept.

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:

  • Must be a static class.
  • Must contain an extension method of the class that we want to intercept.
  • Must have the InterceptsLocation attribute, with the values for the file path we want to intercept as well as the line number and the character number.

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

  • On line 1, there is the class declaration
  • On lines 3 up to 6, there is the InterceptsLocation attribute.
  • On lines 7 up to 10, there is the extension method for the class we want to intercept.

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:

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:

The character parameter is the position of the character of the method you want to intercept, in this example is the character 22 (you can see this info at the bottom of your Visual Studio):

Now when running the code, the method PrintValue2 will be intercepted, and instead of being executed, it will actually execute the interceptor method (the “InterceptMethodPrintValue2” that we created previously). This is the output:

var interceptableExample = new InterceptableExample();

interceptableExample.PrintValue1();
interceptableExample.PrintValue2();
interceptableExample.PrintValue3();

// Output:
Test 1
Interceptor is here!
Test 3

Note that instead of printing the message Test 2, the message Interceptor is here! was printed.

Conclusion

Interceptors were introduced on C# 12 to be used with source generators, to modify instead of adding code to an existing source compilation. With an interceptor, you can then substitute calls to an interceptable method with a call to the interceptor method.

This is the link for the project in GitHub: https://github.com/henriquesd/DotNet8Examples

If you like this demo, I kindly ask you to give a ⭐️ in the repository.

Thanks for reading!

References

What’s new in C# 12 — Microsoft Docs

GitHub — DotNet Roslyn

--

--