Why can't I define top-level extension methods in C# 9?

Viewed 584

I had thought that using the Top-Level Statements feature in C# 9 essentially wraps your top-level code in the usual Program class and Main method.

A decompiled top-level program looks like this:

[CompilerGenerated]
internal static class $Program
{
    private static void $Main(string[] args)
    {
        // top-level code here
    }
}

You can define normal methods at the top-level. They are compiled into the Program class, but outside of the Main method, where extension methods can also be defined.

Because the generated Program class is static and non-generic, I would expect to be able to define extension methods at the top level. However, I get compiler error CS1106: Extension method must be defined in a non-generic static class

VS Code compiler error CS1106: Extension method must be defined in a non-generic static class

Why is this?

5 Answers

The C# Language Specification says:

When the first parameter of a method includes the this modifier, that method is said to be an extension method. Extension methods can only be declared in non-generic, non-nested static classes.

According to the language specification, extension methods must be declared in a static class.

It does not matter that top-level methods are implemented by placing them in a hidden static class. Top-level methods are (by definition) not declared in any class, and therefore cannot be extension methods according to the specification.

As with all language design questions, this is the way that it is because this is the way that the language design team designed the language. Presumably the same concerns which prevent you from defining extension methods inside non-static classes also apply to top-level methods.

You can open a discussion in the csharplang repo or ask a question on Gitter if you want someone with more authority to possibly give more detail.

The top-level statement feature is implemented the way you described it: your top level code is wrapped into the compiler generated class and Main method.

Your extension cannot be declared inside this Main method, so your syntax is invalid. An extension declaration is not a top-level statement.

Using SharpLab we can see that, yes, the generated Program is static, and multiple methods declared in a top level context do get compiled correctly (example here), something is missing when comparing to extension methods: The [Extension] attribute, as both the class and method need to be marked with this, official docs here.

This is the code generated by a top level Hello World (some things omited for brevity, full thing here)

[assembly: Extension]
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[CompilerGenerated]
internal static class <Program>$
{
    private static void <Main>$(string[] args)
    {
        Console.WriteLine("Hello World");
    }
}

Now here's the decompiled code for a class with an extension method:

[Extension]
internal static class Foo
{
    [Extension]
    private static void WriteToConsole(string source)
    {
        Console.WriteLine(source);
    }
}

Seems like the compiler isn't recognizing that there are extension methods, and as such isn't compiling it correctly. We also can't forcibly place the Extension attribute, as we then get an error saying that

error CS1112: Do not use 'System.Runtime.CompilerServices.ExtensionAttribute'. Use the 'this' keyword instead

And even if we could, we can't place it on the Program class itself

TL;DR: The C# compiler doesn't recognize that there are extension methods in the top level statements, and as such doesn't apply the necessary [Extension] attribute to the class or the method

To attempt and answer to my own question with a suspicion.

I wonder if this was a design choice, and that top-level methods are analogous to Local Functions from C# 7.

With a local function, methods are also compiled into the class but outside of the method they are defined in the source code. The generated method names also look very similar, like <$Main>g__Test.

Attempting to write the same extension method as a local function gives me the same compiler error:

Local function error CS1106: Extension method must be defined in a non-generic static class

According to Mads Torgersen in this video from .NET Conf 2020 any functions declared as top-level statements are local functions within the main() method, rather than methods on the Program class.

Local functions cannot be extension methods.

Related