A namespace groups types under a name so that types in different libraries do not clash (C# specification clause 14). Namespaces are not tied to files, folders or assemblies. One namespace can be spread over many files and assemblies, and one file can contribute to several namespaces. Using directives are on the File structure page.
Namespace declarations
namespace_declaration:
namespace qualified_identifier namespace_body [;]
namespace_body:
{ {extern_alias_directive} {using_directive} {namespace_member_declaration} }
namespace Shop.Billing
{
using System.Text; // applies inside this namespace only
public class Invoice { }
namespace Internal // Shop.Billing.Internal
{
class Ledger { }
}
}
- A dotted name is shorthand for nesting:
namespace A.B { }is the same asnamespace A { namespace B { } }. - Several declarations with the same fully qualified name contribute to one namespace, in the same file or in different ones. A type name must still be unique in it.
- Namespaces are always implicitly public. A namespace declaration can carry no access modifiers.
- A namespace declaration may be at the top of a compilation unit or inside another namespace declaration, but not inside a type.
File-scoped namespaces
namespace Name; (C# 10) puts the rest of the file in one namespace without
braces.
using System;
namespace Shop.Billing;
using System.Text; // these using directives are inside the namespace
public class Invoice { }
public class Receipt { }
- A file can have only one file-scoped namespace.
- It must come before any type declaration in the file.
- It cannot appear with a block-scoped namespace in the same file, or with top-level statements.
- Extern aliases and using directives may come before it, and using directives after it are inside the namespace.
The global namespace
Types declared outside any namespace belong to the global namespace, which
has no name. Top-level statements are in it too. Any name can be reached from it with
the global:: qualifier.
Namespace members
A namespace may contain only namespaces and type declarations: classes, structs, interfaces, enums, delegates and records, and, in C# 15, unions (clause 14.7). Unlike languages such as C++, it cannot hold fields, functions or variables directly.
A type directly in a namespace can be public or internal,
and is internal if neither is written (clause 7.4.2). Nested types have more
options; see Classes.
Name lookup
A simple name in a namespace is looked up from the innermost scope outwards (clause 7.7). At each namespace level the compiler checks the namespace’s own members first and then its using directives, and a member of the namespace wins over an imported name. A name found in two imported namespaces at the same level is ambiguous and is an error only if used.
Because lookup goes outwards, a class named System in the current
namespace hides the real System namespace. global::System always
reaches the real one.
The :: qualifier
The :: token looks a name up in an alias or the global namespace, and
never in a type (clause 14.9).
using Col = System.Collections.Generic;
Col::List<int> list = new(); // alias
global::System.Console.WriteLine(); // from the global namespace
The left side must be global or an extern or using alias.
global means the global namespace, even if something else is called
global.
Extern aliases
Two referenced assemblies can define the same fully qualified type. An extern alias gives one of them its own root, so that both can be used (clause 14.4). The alias is declared in the project, then named in the file.
// In the .csproj:
// <Reference Include="filename.dll"><Aliases>Legacy</Aliases></Reference>
extern alias Legacy; // comes before everything else
class Program
{
Legacy::Old.Api.Client client = new();
}
Extern alias directives must come before all using directives in the same scope.
The alias global is predefined and refers to the default root, which is
where assemblies without an alias are placed.