S9415 — Local functions should be marked "static" when they don't capture variables

Language
C#
Type
Code smell
Severity
Major
Tags
confusing, idiomatic

This rule raises an issue when a local function does not capture any variables from its enclosing scope and is not marked as static.

Why is this an issue?

Local functions can access variables from their enclosing scope (known as "capturing" or "closing over" variables). When a local function doesn’t capture any variables, it is completely independent of the surrounding function’s state.

C# lets you make this independence explicit by marking a non-capturing local function static, a modifier introduced for local functions in C# 8.0. Marking a local function static communicates to other developers that:

Without this explicit marking, readers must examine the entire function body to determine whether it captures variables, and a future change might accidentally introduce a capture without anyone noticing.

Once a local function is marked static, the compiler enforces this independence for good: any later attempt to reference a variable from the enclosing scope fails to compile instead of silently changing the function’s behavior. It also guarantees the local function never allocates a closure object at runtime - a guarantee the compiler can’t give for a local function that simply happens not to capture anything today.

How to fix it

Add the static modifier to the local function declaration. This explicitly marks the function as independent of the enclosing scope.

Code examples

Noncompliant code example


double CircleArea(double radius)
{
    return Math.PI * Square(radius);

    double Square(double value) => value * value; // Noncompliant
}

Compliant solution


double CircleArea(double radius)
{
    return Math.PI * Square(radius);

    static double Square(double value) => value * value;
}

Resources

Documentation

↑ Back to top