Inconsistent accessibility error when exposing a file-scoped type from a public method
0 reputation · 18 Apr 2020, 14:13 UTC
0 reputation · 18 Apr 2020, 14:13 UTC
When designing helper types that should not be accidentally exposed outside their source file, the file modifier offers stronger encapsulation than internal. However, if a member such as a public method returns or accepts that file‑scoped type, the compiler flags an accessibility‑domain inconsistency because the member’s effective domain is broader than the type’s domain. The decision to use file versus internal must consider future needs for partial declarations, source‑generated code, or cross‑file usage within the same assembly, as well as the required C# language version and SDK support.
What are the practical trade‑offs between file and internal for helper types that may later need partial declarations in another file? How does the accessibility‑domain rule behave when a source generator emits a file‑scoped type that is referenced from a member in a different file? Under what project constraints should a team prefer file despite the need for cross‑file accessibility?
34775 reputation · 19 Apr 2020, 02:06 UTC
Choose internal when a helper type might later need a partial declaration in another file, when a source generator emits the type in one file and a hand-written member consumes it in another, or when you cannot guarantee the type will stay file-private. Choose file only when the type is a true implementation detail, every part and consumer remains in the same source file, and the project uses C# 11 or later. If a public member returns or accepts a file-scoped type, the compiler reports CS0052 because the member’s accessibility domain is wider than the type’s domain.
The accessibility-domain rule requires that any type exposed in a member signature be at least as accessible as the member itself. A public method exposes its parameter and return types to every assembly that can reference the containing class. A file type is accessible only inside its declaring source file, so callers outside that file cannot name the type. The same mismatch occurs for internal members on public types when the parameter type is file-scoped: the member’s effective domain still crosses the file boundary.
file partial types require every part in the same file. Moving one part to another file makes the compiler treat it as a separate, unrelated type. A source generator that emits a file-scoped type into a generated file cannot be consumed by a method in a separate user file unless the generator also emits the consuming member into that same generated file. Otherwise CS0052 appears. Making the generated type internal removes the boundary problem while keeping the type invisible outside the assembly.
A team should prefer file only if the helper never appears in a public signature, the consuming code can be co-located in the same file, or cross-file access goes through an internal facade that wraps the file type without returning it in a signature. If the type must cross files in signatures, file is the wrong tool and internal is the minimal fix.
FileA.cs, declare file class Helper {}.FileB.cs, add public Helper GetHelper() => new();.internal class Helper or the method to internal Helper GetHelper(), then rebuild; the error should disappear.To confirm compiler support, run dotnet --info or csc -version and check that the compiler reports version 11.0 or higher. Earlier compilers reject the file keyword before accessibility analysis runs.
Do the partial declarations or generated consumers cross assembly boundaries? This determines whether internal is sufficient or whether you need public with InternalsVisibleTo.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.