ZetCode

C# namespace

last modified October 17, 2026

In this article we cover namespaces in C#.

Namespaces are used to organize code at the highest logical level. They classify and present programming elements that are exposed to other programs and applications. Within a namespace, we can declare another namespace, a class, a record, an interface, a structure, an enumeration or a delegate.

We cannot define items such as methods, properties, fields, and events directly in a namespace. These items must be declared within types such as structures or classes. Namespaces prevent name conflicts and simplify references when using large groups of types.

Namespaces organize types in an assembly. An assembly is a reusable, versionable and self-describing building block of a .NET application. Assemblies can contain multiple namespaces, and a namespace can be spread over multiple assemblies. Namespaces can contain other namespaces. An assembly provides a fundamental unit of physical code grouping. A namespace provides a fundamental unit of logical code grouping.

Some programming languages use modules or packages for the same purpose.

C# create a namespace

The namespace keyword is used to declare a namespace in C#. The name of the namespace must be a valid C# identifier. The parts of a nested namespace name are separated with the . character, for instance System.Collections.Generic. The using directive removes the requirement to specify the name of the namespace for every type.

namespace ZetCode.Shapes
{
    class Circle { }
}

A block-scoped namespace applies to the types declared between its curly braces.

namespace ZetCode.Shapes;

class Circle { }

C# 10 introduced file-scoped namespaces. When we use the namespace keyword without curly braces, the namespace applies to the entire file. This saves one level of indentation, and it is the usual form in modern code. A file can contain only one file-scoped namespace, and it cannot be combined with block-scoped namespaces.

C# file-scoped namespace

The following example uses a file-scoped namespace.

Program.cs
namespace ZetCode.Shapes;

record Circle(double Radius)
{
    public double Area() => Math.PI * Radius * Radius;
}

class Program
{
    static void Main()
    {
        var c = new Circle(3);

        Console.WriteLine(c.GetType().FullName);
        Console.WriteLine($"{c.Area():F2}");
    }
}

Both the Circle record and the Program class belong to the ZetCode.Shapes namespace.

Console.WriteLine(c.GetType().FullName);

The full name of a type includes its namespace.

$ dotnet run
ZetCode.Shapes.Circle
28.27

C# namespaces and top-level statements

Top-level statements always belong to the global namespace. A file with top-level statements cannot have a file-scoped namespace, because the file-scoped namespace must precede all other members in a file. We can, however, declare block-scoped namespaces after the top-level statements.

Program.cs
using Shapes;

var c = new Circle(3);
Console.WriteLine(c.Area());

namespace Shapes
{
    record Circle(double Radius)
    {
        public double Area() => Math.PI * Radius * Radius;
    }
}

The Circle record is declared in the Shapes namespace. The top-level statements import the namespace with the using directive. In larger programs, the types are usually placed in separate files.

$ dotnet run
28.274333882308138

C# built-in namespaces

The built-in libraries are organized within namespaces.

Program.cs
var hostName = System.Net.Dns.GetHostName();
Console.WriteLine($"Hostname: {hostName}");

For instance, the GetHostName method belongs to the Dns class, which is located in the System.Net namespace. To call the method, we use its fully qualified name. A fully qualified name is the name of a type prefixed with the name of the namespace in which the type is defined.

Program.cs
using System.Net;

var hostName = Dns.GetHostName();
Console.WriteLine($"Hostname: {hostName}");

With the using directive, we import the System.Net namespace into our program. Now we can call Dns.GetHostName without explicitly specifying System.Net.

C# implicit and global usings

How come we can use Console without importing the System namespace? Projects created with dotnet new have the <ImplicitUsings>enable</ImplicitUsings> setting in the project file. With this setting, the SDK generates a file with global using directives for the most common namespaces. For a console application, the file looks like this:

// <auto-generated/>
global using System;
global using System.Collections.Generic;
global using System.IO;
global using System.Linq;
global using System.Net.Http;
global using System.Threading;
global using System.Threading.Tasks;

A global using directive applies to all files of the project. We can declare our own global usings; they are often placed in a separate file.

Usings.cs
global using System.Text;
global using static System.Math;

We import the System.Text namespace for the whole project. The using static directive imports the static members of a type, so we can call them without the type name.

Program.cs
var sb = new StringBuilder();

sb.Append($"Square root of 2: {Sqrt(2):F4}");
sb.Append(", ");
sb.Append($"2 to the power of 10: {Pow(2, 10)}");

Console.WriteLine(sb);

We use the StringBuilder class and call Math.Sqrt and Math.Pow without the Math prefix.

$ dotnet run
Square root of 2: 1.4142, 2 to the power of 10: 1024

C# sharing namespace

In the following code, we have two files that share the same namespace.

Counter.cs
namespace Sharing;

class Counter
{
    public int Value { get; private set; }

    public void Inc()
    {
        Value += 100;
        Console.WriteLine(Value);
    }
}

We have a Sharing namespace. In the namespace, we have a Counter class.

namespace Sharing;

We declare a namespace called Sharing. The namespace applies to the current file.

Program.cs
namespace Sharing;

public class Program
{
    static void Main()
    {
        var counter = new Counter();

        counter.Inc();
        counter.Inc();
    }
}

In the Program class, we work with the Counter class from the previous file. We invoke its Inc method.

namespace Sharing;
...

We work in the same namespace.

var counter = new Counter();

counter.Inc();
counter.Inc();

We create an instance of the Counter class. We call its Inc method twice. Because both types are in the same namespace, we do not need to specify its name or import it.

$ dotnet run
100
200

C# distinct namespaces

The following code example has two distinct namespaces. We use the using directive to import types from a different namespace.

Basic.cs
namespace Mathematical;

public class Basic
{
    public const double PI = 3.141592653589;

    public static double GetPi()
    {
        return PI;
    }
}

In the Basic class, we define a PI constant and a GetPi method. The Basic class is defined within the Mathematical namespace.

Program.cs
using Mathematical;

Console.WriteLine(Basic.PI);
Console.WriteLine(Basic.GetPi());

Console.WriteLine(Mathematical.Basic.PI);
Console.WriteLine(Mathematical.Basic.GetPi());

In this file, we use the types from the Mathematical namespace.

using Mathematical;

We import the types from the Mathematical namespace.

Console.WriteLine(Basic.PI);
Console.WriteLine(Basic.GetPi());

Now we can use those types. In our case it is the Basic class.

Console.WriteLine(Mathematical.Basic.PI);
Console.WriteLine(Mathematical.Basic.GetPi());

Another way to access the types is to specify their fully qualified names. This works even without the using directive.

$ dotnet run
3.141592653589
3.141592653589
3.141592653589
3.141592653589

C# global namespace

The global namespace is the root of all namespaces; the System namespace and all other top-level namespaces are its members. It may happen that we create a type or a namespace that conflicts with one from .NET. In such cases, we can refer to the global namespace with the global:: alias.

Program.cs
namespace ZetCode;

class System
{
    public override string ToString() => "This is System class";
}

public class Program
{
    static void Main()
    {
        var sys = new System();

        // System.Console.WriteLine(sys); error CS0117
        global::System.Console.WriteLine(sys);
    }
}

In our ZetCode namespace, we create a System class that clashes with the System namespace from .NET. (Note that it is not a good idea to create such a type.)

var sys = new System();

Here we refer to the System class from the ZetCode namespace.

// System.Console.WriteLine(sys); error CS0117
global::System.Console.WriteLine(sys);

Inside the ZetCode namespace, the System name refers to our class, so System.Console does not compile. With the global:: prefix, we point to the System namespace of the global namespace.

$ dotnet run
This is System class

C# default namespace

Types that are not declared in any namespace belong to the global namespace.

Program.cs
struct Book
{
    public override string ToString() => "Book struct in the global namespace";
}

namespace MainProgram
{
    struct Book
    {
        public override string ToString() => "Book struct in the MainProgram namespace";
    }

    public class Program
    {
        static void Main()
        {
            var book1 = new Book();
            var book2 = new global::Book();

            Console.WriteLine(book1);
            Console.WriteLine(book2);
        }
    }
}

We have two Book structures; one is defined in the MainProgram namespace, the other is defined outside this namespace.

struct Book
{
    public override string ToString() => "Book struct in the global namespace";
}

This Book structure is defined outside of the MainProgram namespace. It belongs to the global namespace.

var book1 = new Book();

We refer to the structure defined inside the MainProgram namespace. Names are looked up in the enclosing namespace first.

var book2 = new global::Book();

With the global:: prefix we point to the structure defined outside the MainProgram namespace.

$ dotnet run
Book struct in the MainProgram namespace
Book struct in the global namespace

C# using alias

The using directive can also create an alias for a namespace or a type. With nested namespaces, the fully qualified names might get long. We can shorten them by creating aliases. Since C# 12, an alias can be created for any type, including tuples and arrays.

Program.cs
using ZI = ZetCode.Items;
using Book = ZetCode.Items.Book;
using Point = (int X, int Y);

var book1 = new ZetCode.Items.Book();
var book2 = new ZI.Book();
var book3 = new ZI::Book();
var book4 = new Book();

Console.WriteLine(book1);
Console.WriteLine(book2);
Console.WriteLine(book3);
Console.WriteLine(book4);

Point p = (3, 4);
Console.WriteLine($"x: {p.X}, y: {p.Y}");

namespace ZetCode.Items
{
    class Book
    {
        public override string ToString() => "This is a book";
    }
}

In the example, we create aliases for a namespace, a class, and a tuple type.

namespace ZetCode.Items
{
    class Book
    {
        ...
    }
}

The ZetCode.Items declaration is a shorthand for a ZetCode namespace with a nested Items namespace. The fully qualified name of the Book class is ZetCode.Items.Book.

using ZI = ZetCode.Items;
using Book = ZetCode.Items.Book;
using Point = (int X, int Y);

We create a ZI alias for the namespace, a Book alias for the class, and a Point alias for a named tuple.

var book2 = new ZI.Book();
var book3 = new ZI::Book();

The namespace alias can be used with the . or with the :: qualifier. The :: qualifier guarantees that the name on its left is looked up as an alias, even if a type with the same name exists.

$ dotnet run
This is a book
This is a book
This is a book
This is a book
x: 3, y: 4

Source

Declare namespaces to organize types

In this article we covered C# namespaces.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.