Namespaces

Intro

A namespace is a logical scope for organizing types (classes, structs, interfaces, enums, delegates) in C#. It prevents naming collisions and makes large solutions easier to navigate by grouping related code into a clear, discoverable API surface. Encapsulation and versioning boundaries come from assemblies and access modifiers, not namespaces.

Namespaces provide:

  1. Disambiguation for type names (User can exist in multiple namespaces)
  2. Logical modularization of large codebases
  3. Better readability and discoverability in tooling

Example (block-scoped namespace):

namespace MyProject.Utilities
{
    public static class MathUtility
    {
        public static int Add(int a, int b) => a + b;
    }
}

Example (file-scoped namespace):

namespace MyProject.Utilities;

public static class MathUtility
{
    public static int Add(int a, int b) => a + b;
}

Pitfalls

Namespace collision between libraries — when two NuGet packages define the same fully qualified type name, the compiler reports ambiguity. A real case: migrating from System.Data.SqlClient to Microsoft.Data.SqlClient while both packages are referenced causes every SqlConnection usage to error. Fix with extern alias in the project file, or complete the migration before removing the old package.

Global using overreach — C# 10 global using directives apply to every file in the project. A team added global using Newtonsoft.Json; and global using System.Text.Json; simultaneously, causing ambiguity errors on JsonSerializer across 200+ files. Limit global usings to universally unambiguous imports (System, System.Collections.Generic); keep domain-specific namespaces in per-file using directives.

Namespace/assembly mismatch — the compiler does not enforce alignment between namespace name and assembly name. A type in MyApp.Utilities can live in MyApp.Core.dll. Misaligned namespaces cause autocompletion to mislead developers about which assembly to reference and make dotnet add package discovery harder.

Tradeoffs

Questions


Whats next