In this article, we will learn about the File Access Modifier in C#. We will focus on what problems it solves and how to use it.
Let’s dive in.
What Is the File Access Modifier
We use an access modifier to define the scope and visibility of a class
or its members.
In C#, we use the file
access modifier to restrict the scope and visibility of a top-level type to the file where we declare it. Using this access modifier results in the declaration of types known as file-local types.
Usages and Benefits of the File Access Modifier
Above all, the file
modifier helps:
- hiding implementation details from the rest of the compilation
- avoiding conflicts with already existing types
- source generators use a namespace containing user-defined types without name conflicts
Until now, code generators had to scan existing user-defined types to prevent name conflicts, making code generation time-consuming.
To help speed up source generators and to avoid these possible collisions, the file access modifier was added.
The compiler restricts file-local types to the file. When defining types with the same name but in different files, the compiler ensures that it gives them distinct names to avoid naming conflicts. During name resolution, the file-local type takes precedence within its file.
Additionally, developers can use this modifier while constructing libraries to replace private nested classes employed internally and as a substitute for extension method classes confined to a single file.
Let’s look at this access modifier in more detail.
Working With the File Access Modifier
Only top-level types such as classes, structs, enums, delegates, interfaces, records, and record structs can use the file
access modifier.
However, we can not use a file-local type:
- as a return type or parameter for a member that is more visible than the
file
scope - as a field member of a type more visible than the
file
scope - with other accessibility modifiers besides the default –
internal
- with nested types
Let’s create a ContentUploadService.cs
file:
namespace FileAccessModifierInCsharp; public static class Logger { public static string GetMessage(DateTime datetime, int fileId) { return $"{datetime} - Upload - File ID: {fileId}"; } } public static class ContentUploadService { public static string GetMessage(DateTime datetime, int fileId) { return Logger.GetMessage(datetime, fileId); } }
Here, we make a public ContentUploadService
class and a public static
class named Logger
.
After that, we create another source file named ContentDownloadService.cs,
which also contains a static
type called Logger
and a public ContentDownloadService
class:
namespace FileAccessModifierInCsharp; public static class Logger { public static string GetMessage(DateTime datetime, int fileId) { return $"{datetime} - Download - File ID: {fileId}"; } } public static class ContentDownloadService { public static string GetMessage(DateTime datetime, int fileId) { return Logger.GetMessage(datetime, fileId); } }
Now, we have errors because a public type named Logger
is defined in both files, and both of them also have a method with the same name and parameters:
Error CS0101 The namespace 'Content' already contains a definition for 'Logger.'
Now, to solve this error, we need to change the public
access modifier to file
on at least one of the two Logger
classes, thus restricting its visibility to the file where it’s defined:
file static class Logger
After that, the compiler error disappears, and each type can use the Logger
definition in its file.
Conclusion
In conclusion, we have learned how to use the file access modifier and what it does. The file-local types are beneficial for code generation as they ease the generation of type names without worrying about name conflicts, which is the main reason for introducing this access modifier to C#.