Part of the file/namespace organization epic #6. Category: Maintainability · ID: NE0001
Need & Action (Bedarf & Handlung)
Need: Multiple top-level types (class, struct, record, record struct, interface, enum,
delegate) declared in one file, or a file whose name does not match the type it contains, make types
hard to find and produce noisy diffs.
Action: Report when a file contains more than one top-level type, or when the file name does not
equal the contained type's name. One type ⇒ one file, TypeName ⇒ TypeName.cs.
Generic overloads (default = strict): by default, overloads that share a base name — Result,
Result<T>, Result<T1, T2> — are treated like any other types: each arity must live in its own file,
with the arity encoded in the name (Result.cs, Result{T}.cs, Result{T1,T2}.cs). Grouping them into a
single file named after the base identifier (Result.cs) is opt-in via the build property below.
Non-compliant example
// File: Shapes.cs
namespace Geometry;
public sealed class Circle { } // NE0001: file name 'Shapes' does not match type 'Circle'
public sealed class Square { } // NE0001: more than one top-level type in this file
Compliant example
// File: Circle.cs
namespace Geometry;
public sealed class Circle { }
// File: Square.cs
namespace Geometry;
public sealed class Square { }
// Default (strict): one file per arity
// File: Result.cs -> public readonly struct Result { }
// File: Result{T}.cs -> public readonly struct Result<T> { }
// File: Result{T1,T2}.cs -> public readonly struct Result<T1, T2> { }
// With NetEvolveAnalyzerGroupGenericOverloads = true: grouping into one file is allowed
// File: Result.cs
namespace Geometry;
public readonly struct Result { }
public readonly struct Result<T> { }
public readonly struct Result<T1, T2> { }
Category
Maintainability
Default severity
Warning
Code fix
Maybe — feasible but likely a follow-up. Roslyn already ships a "Move type to X.cs" refactoring;
the fix would (a) move each extra type into its own correctly named file and (b) offer a rename of the
file to match a single contained type.
Configuration & build properties
NetEvolveAnalyzerGroupGenericOverloads (bool, default false) — when false (default), each generic
arity must live in its own arity-encoded file. When true, generic overloads that share a base name may
be grouped into a single file named after the base identifier (Result.cs).
- Global opt-outs from the epic apply: auto-off on
PublishSingleFile=true, plus
NetEvolveAnalyzerDisableFileOrganizationRules.
Edge cases & exceptions
partial types spread across files: the partial's file(s) must still be named after the type; do not
count partials as "multiple types".
- Nested types are ignored (only top-level declarations count).
- Generated code (
generated_code = true, *.g.cs, *.designer.cs) is skipped.
- File-scoped vs. block-scoped namespaces are treated identically.
- Arity-encoded file names when strict (default): confirm the encoding convention —
Result{T}.cs /
Result{T1,T2}.cs (curly braces, as used by StyleCop SA1649's fileNamingConvention: metadata).
Need & Action (Bedarf & Handlung)
Need: Multiple top-level types (
class,struct,record,record struct,interface,enum,delegate) declared in one file, or a file whose name does not match the type it contains, make typeshard to find and produce noisy diffs.
Action: Report when a file contains more than one top-level type, or when the file name does not
equal the contained type's name. One type ⇒ one file,
TypeName⇒TypeName.cs.Generic overloads (default = strict): by default, overloads that share a base name —
Result,Result<T>,Result<T1, T2>— are treated like any other types: each arity must live in its own file,with the arity encoded in the name (
Result.cs,Result{T}.cs,Result{T1,T2}.cs). Grouping them into asingle file named after the base identifier (
Result.cs) is opt-in via the build property below.Non-compliant example
Compliant example
Category
Maintainability
Default severity
Warning
Code fix
Maybe — feasible but likely a follow-up. Roslyn already ships a "Move type to
X.cs" refactoring;the fix would (a) move each extra type into its own correctly named file and (b) offer a rename of the
file to match a single contained type.
Configuration & build properties
NetEvolveAnalyzerGroupGenericOverloads(bool, defaultfalse) — whenfalse(default), each genericarity must live in its own arity-encoded file. When
true, generic overloads that share a base name maybe grouped into a single file named after the base identifier (
Result.cs).PublishSingleFile=true, plusNetEvolveAnalyzerDisableFileOrganizationRules.Edge cases & exceptions
partialtypes spread across files: the partial's file(s) must still be named after the type; do notcount partials as "multiple types".
generated_code = true,*.g.cs,*.designer.cs) is skipped.Result{T}.cs/Result{T1,T2}.cs(curly braces, as used by StyleCop SA1649'sfileNamingConvention: metadata).