C# File Access Modifier: Syntax and Use Cases
c# file access modifier: Understand C# access modifiers like public, private, internal, and file, and how they control visibility across classes and files.
When you write c# file access modifier in a search, you are likely looking for how file fits into the existing access modifier family. C# has had public, private, protected, internal, and protected internal for a long time. The file modifier, introduced in C# 11, restricts a top-level type to the file where it is declared. This closes a gap where a type needed to be visible to multiple types in the same file but invisible everywhere else.
What Does file Actually Restrict?
The file modifier can only be applied to top-level types: classes, structs, interfaces, enums, and delegates. It cannot be applied to nested types, members, or local functions. A file-local type is visible only within the source file where it is declared. This is stricter than internal, which allows visibility throughout the same assembly.
// FileA.cs file class Helper { public static int Compute(int x) => x * 2; } public class Processor { public int Process(int value) => Helper.Compute(value); }
In this example, Helper is only accessible from FileA.cs. If another file in the same project tries to reference Helper, the compiler raises an error because Helper is not visible outside its file. The file modifier exists to prevent naming collisions when multiple files in a large project define helper types with the same name.
How file Compares to internal
internal makes a type visible to all code within the same assembly. file limits visibility to the single source file. This distinction matters when you have a helper type used by several types in one file, but you do not want it to become part of the assembly's public API surface.
| Modifier | Scope | Use Case |
|---|---|---|
public | Any code that can reference the assembly | Public API of a library |
internal | All code in the same assembly | Internal helpers shared across the project |
file | Only the source file that defines the type | One-file helpers, avoiding name collisions |
private | Only the containing type | Implementation details of a class |
When you have a helper class used only by one other class in a single file, file prevents accidental external use. internal would expose it to the whole assembly, which might be more than you need. The choice depends on whether the helper is conceptually part of the assembly's internal contract or purely a local implementation detail.
Applying file to Different Type Kinds
The file modifier works on all top-level type declarations, but you must place it before the type keyword.
file struct Point { public int X { get; set; } public int Y { get; set; } } file interface ILogger { void Log(string message); } file enum Severity { Low, Medium, High } file delegate void AlertHandler(string message);
All of these are valid. You can also combine file with other modifiers? No, file cannot be combined with public, internal, protected, or private. It stands alone because it defines a distinct visibility level. Attempting file internal class produces a compile-time error.
Practical Use: Named Tuples or Record Types
A common pattern is to define a small record or class that is only used as an intermediary in a specific file. Without file, you might pollute the namespace with a type that only one method uses. With file, you can keep the declaration close to its usage.
// In File.cs file record UserProfile(string Username, int Age); public class ProfileService { public UserProfile GetProfile(int id) { // Implementation return new UserProfile("alice", 30); } }
Because UserProfile is file-scoped, another file cannot accidentally reference it. This makes the codebase easier to reason about: if you see a file type, you know you only need to understand that single file to see all its usages.
File-Scoped Namespaces: A Related but Different Feature
C# 10 introduced file-scoped namespaces, which allow you to write namespace MyApp; at the top of a file instead of wrapping every type in braces. That feature changes how namespaces are declared, not how types are visible. It is easy to confuse the two because both involve files. File-scoped namespaces are a syntactic convenience; the file modifier is a visibility control.
When file Can Cause Problems
Because file types are not visible outside their file, you cannot use them as return types or parameters for methods that are not also in the same file. If a public method in FileA.cs returns a file type, any caller outside that file would not know the type, so this is not allowed.
// FileA.cs file class InternalHelper { } public class Exposer { // Error: Inconsistent accessibility public InternalHelper GetHelper() => new InternalHelper(); }
The compiler enforces that a method's accessibility cannot exceed that of its parameter and return types. Since InternalHelper is file, it is less accessible than public, so the method cannot expose it. You need to either change the method to internal and the return type to internal, or keep the helper entirely private to the class.
Compatibility and Tooling Considerations
file requires C# 11 or later, which means you need at least .NET 7 SDK or a NuGet package that supports C# 11 language features. Most modern projects can enable this without issue. However, if you are working on an older codebase or using a tool that compiles with an earlier compiler, file is not available. You can use internal or private nested classes in those cases, though you lose the file-level restriction.
Another operational point: reflection can still see file types, but they are marked with an internal accessibility. Tools like source generators may interact with them differently because the generated code might need to reference the file type from another file, which is impossible. If you use source generators, avoid placing generated references to file-local types.
Maintainability Tradeoffs
Using file keeps large files self-contained. When you open a file with several helper types, you immediately know they are not shared. That reduces the risk of unintended coupling across the assembly. However, if the helper becomes useful in another file, you must change its modifier to internal or public, which is a deliberate refactoring step. That is generally good: it forces you to think about whether you want to expose the type beyond its file.
On the other hand, overusing file can make a project harder to navigate because types are not discoverable through IntelliSense outside their file. A developer searching for a type name in another file will not see it, which could be confusing if they expect it to be available. Use file for truly local helpers, not for types that might naturally be reused later.
Final Decision Guidance
When you have a type that is only used within one source file, file is the precise modifier. When the type is used by multiple files within the same assembly, use internal. When you are building a library and want the type visible to consumers, use public. You might also consider private nested classes if the type is only used inside one class and you prefer not to have a top-level type at all. file sits between private and internal in scope, offering a level of encapsulation that aligns with C#'s emphasis on explicit visibility control.