Introduced in C# 11.0, the file modifier lets you define top-level types that are accessible only within the current file. This keeps helper types close to the code they support without exposing them across the assembly.
Why it matters
File-local types improve maintainability when a helper is too large for a nested private type but still should not be part of your shared surface area. They reduce namespace clutter and make intent explicit: this type exists only to support this file.
Cautions
File-local types are not visible outside their file, so avoid them for anything shared across multiple classes or test projects. Also keep naming clear: because file-local and public types can live together, concise names with obvious responsibilities prevent confusion during refactors.
Declaring helper types with the file modifier
File-local types keep implementation details in the same file while preventing those types from leaking into project-wide namespaces.
Valid since C# 11.0
using System;
public sealed class UserCache
{
public string CreateCacheKey(int userId, DateTime snapshotUtc)
{
var key = new UserCacheKey(userId, snapshotUtc);
return key.ToString();
}
}
file readonly record struct UserCacheKey(int UserId, DateTime SnapshotUtc)
{
public override string ToString() => $"user:{UserId}:snapshot:{SnapshotUtc:yyyyMMddHHmmss}";
}
public static class Program
{
public static void Main()
{
var cache = new UserCache();
string key = cache.CreateCacheKey(42, new DateTime(2026, 7, 10, 9, 30, 0, DateTimeKind.Utc));
Console.WriteLine(key);
}
}
From nested private classes to file-local collaborators
Move deeply nested helper types out of containing classes while keeping them hidden from other files through the file modifier.
Valid since C# 11.0
Without var
using System;
using System.Collections.Generic;
using System.Globalization;
public sealed class ReportFormatter
{
public string Format(IReadOnlyList<decimal> values)
{
var rowFormatter = new RowFormatter();
return rowFormatter.Format(values);
}
private sealed class RowFormatter
{
public string Format(IReadOnlyList<decimal> values)
{
return string.Join(", ", values).Replace(",", CultureInfo.InvariantCulture.NumberFormat.NumberGroupSeparator);
}
}
}
public static class Program
{
public static void Main()
{
var formatter = new ReportFormatter();
Console.WriteLine(formatter.Format(new[] { 12.5m, 48.3m, 105.7m }));
}
}
With var
using System;
using System.Collections.Generic;
using System.Globalization;
public sealed class ReportFormatter
{
public string Format(IReadOnlyList<decimal> values)
{
var rowFormatter = new RowFormatter();
return rowFormatter.Format(values);
}
}
file sealed class RowFormatter
{
public string Format(IReadOnlyList<decimal> values)
{
var formatted = new List<string>(values.Count);
foreach (decimal value in values)
{
formatted.Add(value.ToString("N1", CultureInfo.InvariantCulture));
}
return string.Join(" | ", formatted);
}
}
public static class Program
{
public static void Main()
{
var formatter = new ReportFormatter();
Console.WriteLine(formatter.Format(new[] { 12.5m, 48.3m, 105.7m }));
}
}