C# 12: The Experimental Attribute

In the previous blog posts of the C# 12 series you learned about different C# 12 features:

In this blog post, you will learn about another C# 12 feature: The Experimental Attribute.

When you write code, especially when you build a library that is used by many other developers and projects, you might want to flag some features as experimental. Why? Because sometimes you have an idea, but you’re not 100% sure about the API and the implementation. This means the code might change or be removed in the future. But how do you tell this to developers that consume your library? Somehow they should know which features are experimental, and exactly this is where the Experimental attribute enters the stage.

The Experimental Attribute

Since .NET 8, you can use the Experimental attribute (Namespace: System.Diagnostics.CodeAnalysis) to mark a member like a property or a method as experimental. When you do this, a consumer of the marked code will get a compile-time error. This ensures that they are aware that this is experimental code. To use the code, they need to explicitly suppress the error. Let’s take a look at this.

An Example

Let’s create a simple solution with a console application and a class library project. As you can see in the screenshot below, the console application references the class library project:

Now let’s take a look at the Person class that I defined in the class library project. As you can see in the code snippet below, it has a primary constructor with the string parameters firstName and lastName. The created fields with the same names are used as return values for the two expression-bodied properties FirstName and LastName. Then there is also a FullName property that returns an interpolated string. But the interesting part is the Experimental attribute on the FullName property. It marks this property as experimental. To use the Experimental attribute, a using directive for the namespace System.Diagnostics.CodeAnalysis is defined at the top. With the attribute you specify a Diagnostic ID, which is a string that identifies your feature, in this case Thomas001.

using System.Diagnostics.CodeAnalysis;

namespace MyClassLib;

public class Person(string firstName, string lastName)
{
    public string FirstName => firstName;
    public string LastName => lastName;

    [Experimental("THOMAS001")]
    public string FullName => $"{FirstName} {LastName}";
}

In the console application, the class library is referenced. This means that you might think that you can use the Person class there in the Program.cs file like you see it in the code snippet below.

using MyClassLib;

var person = new Person("Thomas", "Huber");

Console.WriteLine(person.FullName);

But as you know, the FullName property is marked as experimental, and because of this, Visual Studio underlines it like in the screenshot below in red. When you hover over it, you see the error that says that the FullName property is for evaluation purposes only and is subject to change or removal in future updates. You also see the THOMAS001 Diagnostic ID that I defined with the Experimental attribute.

When you try to build the project, you get this compile-time error like below in the Error List window.

This means, by default, a developer cannot compile their project with your experimental feature, which is great. Before I show you how to compile it, let’s take a look at another feature of the Experimental attribute.

Besides the Diagnostic ID, in this case THOMAS001, you can also define a URL with custom information. When you click in the code editor or in the Error List window on the THOMAS001 error, Visual Studio will navigate to this url that explains how to resolve warnings related to language features and versions.

Instead of using that default URL, you can use a custom URL for your experimental feature. To define a custom URL, you use like below the UrlFormat property of the Experimental attribute.

[Experimental("THOMAS001", UrlFormat = "https://www.thomasclaudiushuber.com")]
public string FullName => $"{FirstName} {LastName}";

Now, when a developer clicks on the THOMAS001 error in their code editor or in the Error List window, the URL www.thomasclaudiushuber.com will be opened in their browser. This feature allows you to define error-specific documentation.

OK, so far so good. Now let’s look at how to use experimental features in your code.

Use an Experimental Feature

To use an experimental feature in your code, you must suppress the warning/error. There are different ways to do this. One way is to use a #pragma warning in your code with the corresponding Diagnostic ID. #pragma is a so-called preprocessor directive, which is a special instruction for the C# compiler. The code snippet below shows how to suppress the warning/error for our experimental feature with a #pragma preprocessor directive. Before using the FullName property, the warning/error for the Diagnostic ID THOMAS001 is disabled with a #pragma warning disable statement. After the statement that uses the FullName property, the warning/error for the Diagnostic ID THOMAS001 is restored with a #pragma warning restore statement. Of course, between disabling and restoring the warning/error, you can have multiple lines of C# code. Now the code below compiles successfully, as you explicitly disabled warnings/errors for the experimental feature THOMAS001.

using MyClassLib;

var person = new Person("Thomas", "Huber");

#pragma warning disable THOMAS001
Console.WriteLine(person.FullName);
#pragma warning restore THOMAS001

Instead of using a #pragma warning preprocessor directive to use an experimental feature, you can also disable the warning/error for the whole project. To do this, you open your .csproj file and you add like in the code snippet below a NoWarn element. Within this element you specify the Diagnostic ID that you want to disable.

<PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <NoWarn>THOMAS001</NoWarn>
</PropertyGroup>

If you have multiple Diagnostic IDs that you want to disable for your project, you can separate them with commas inside of the NoWarn element:

<NoWarn>THOMAS001,THOMAS002,THOMAS003</NoWarn>

That’s it. Now you can successfully compile your code that uses an experimental feature.

Summary

As you learned in this blog post, the Experimental attribute is a very simple, but also a very powerful tool for library developers. It allows you to include features in your library that are not in a stable state yet, and that might change or might be removed in the future. Of course, you could have implemented experimental features already before .NET 8.0 and C# 12. But with the Experimental attribute the consumers of your library, which are other developers, get nice compiler errors when they use an experimental feature, and they have to explicitly disable that error to be able to compile their projects.

Thanks for reading, I hope you liked it.

Thomas

Share this post

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.