Marking API's as obsolete or as experimental

03/11/2022
C#API.NET

Often times your API in your program or library evolves. So you will need a mechanism for telling that a specific API (an interface or just a simple method call) is obsolete and might be not there anymore in the next major version.

Also, it can happen that you have a preview version of an API, which might not be rock-stable and the API-surface might change. How do we indicate that to the user?

The simple answer: Attributes. .NET knows two special ones in this case:

  • ObsoleteAttribute
  • RequiresPreviewFeaturesAttribute

They are a bit special.

ObsoleteAttribute

The ObsoleteAttribute is in the Framework since almost the beginning. It marks that the element should not be used anymore. The attribute is special because the compiler knows of it and treats it in a certain way. Here is a quick example of how to use it:

OldWarning(); // Generates warning (or error if you enable treat warnings as errors)
OldError(); // Generates errors

[Obsolete($"Take {nameof(New)} instead", false)]
void OldWarning() { }

[Obsolete($"Take {nameof(New)} instead", true)]
void OldError() { }

void New() { }

The first argument is a message to a user, which you can use to indicate, which new method he should use instead. The second one is about whether or not the compiler should treat it as a warning (which is the default, if you don't provide that parameter) or even as an error. The compiler tells you the following: "CS0618: Local function 'OldWarning()' is obsolete: Take New instead".

So you can make a 3 step approach:

  1. Mark it as warning
  2. Mark it as error
  3. Remove it entirely

By the way the attribute can also be set on a class, property, interface or struct. You are not limited to use it on methods.

RequiresPreviewFeaturesAttribute

The RequiresPreviewFeaturesAttribute is used to indicate that a feature is in a preview state. Feature can mean a simple method or a whole class or interface. The usage is roughly the same:

New();

[RequiresPreviewFeatures("API might change")]
void New() { }

If you hover over the New() call you get the message: "API might change". If you try to compile that, you get even an error: "Program.cs(1, 1): [CA2252] API might change Using 'New' requires opting into preview features. See https://aka.ms/dotnet-warnings/preview-features for more information."

The resolve that you have to opt-in into preview features. This is done on project level:

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <TargetFramework>net7.0</TargetFramework>

        <!-- This will opt in into all preview features -->
        <EnablePreviewFeatures>true</EnablePreviewFeatures>
    </PropertyGroup>

Unfortunately it isn't very fine grained. You don't have control over which features you want to use. The (maybe ugly) alternative is using pragmas:

#pragma warning disable CA2252
New();
#pragma warning restore CA2252

In any way, this is until now (.net 7) the only way of marking an API as a preview. In contrast to the ObsoleteAttribute, which is handled by the compiler, the RequiresPreviewFeaturesAttribute is handled via Roslyn-Analyzer.

Conclusion

I showed you a way of marking your APIs as old so you can delete them gracefully later and I also showed you how to mark them as experimental so your user knows, that the API surface is not stable.

4
An error has occurred. This application may no longer respond until reloaded. Reload x