Skip to content

C# - Namespaces

In C#, a namespace is a fundamental concept for organizing and structuring your code. Think of it like a system of folders on your computer. Just as you use folders to group related files, you use namespaces to group related classes, structs, interfaces, and other types. This prevents naming collisions, where two different types might accidentally share the same name.

You define a namespace using the namespace keyword. There are two common ways to do this:

The traditional syntax encloses the code within curly braces {}. This is useful for defining multiple namespaces in a single file.

namespace MyCompany.MyApplication.Services
{
// Code for the Services feature, e.g., classes, interfaces
public class NotificationService
{
// ...
}
}

2. File-Scoped Namespaces (Modern - C# 10 and later)

Section titled “2. File-Scoped Namespaces (Modern - C# 10 and later)”

Modern C# development recommends using file-scoped namespaces. This cleaner syntax declares that all types within the file belong to the specified namespace, reducing indentation and boilerplate code.

namespace MyCompany.MyApplication.Data;
// All types in this file are now in the MyCompany.MyApplication.Data namespace
public class UserRepository
{
// ...
}

To use a type from a namespace, you must provide its fully qualified name, which includes the namespace and the type name, separated by a dot (.):

// Using a fully qualified name
MyCompany.MyApplication.Data.UserRepository repo = new MyCompany.MyApplication.Data.UserRepository();

Writing fully qualified names everywhere is tedious. The using directive allows you to import all the types from a namespace, so you can refer to them directly by their type name. This is standard practice in all C# projects.

using System;
using MyCompany.MyApplication.Data; // Import the Data namespace
using MyCompany.MyApplication.Services; // Import the Services namespace
// Now we can use the types directly
var userRepository = new UserRepository();
var notificationService = new NotificationService();
Console.WriteLine("Services initialized successfully.");

To avoid repeating the same using directives in every file, you can use a global using directive. This makes the namespace available across your entire project. These are typically placed in a central file, like GlobalUsings.cs.

// In GlobalUsings.cs
global using System;
global using MyCompany.MyApplication.Models;

Namespaces can be nested to create a clear hierarchy. The standard convention is CompanyName.ProductName.FeatureName.

namespace MyCompany.CoolApp.Authentication.OAuth; // Nested namespace
public class GoogleAuthHandler
{
// ...
}

Sometimes, two namespaces might contain a type with the same name. This creates an ambiguity. You can resolve this by creating a namespace alias with the using directive.

using System;
using ProjectA.Widgets;
// Both namespaces have a 'Widget' class, causing a conflict.
// using ProjectB.Widgets;
// Create an alias to resolve the conflict
using ProjectBWidget = ProjectB.Widgets.Widget;
var widgetA = new Widget(); // This is ProjectA.Widgets.Widget
var widgetB = new ProjectBWidget(); // This is ProjectB.Widgets.Widget
Console.WriteLine($"Created a widget of type {widgetA.GetType().FullName}");
Console.WriteLine($"Created a widget of type {widgetB.GetType().FullName}");
  • Use File-Scoped Namespaces: Prefer the namespace ...; syntax in new projects to keep your files clean.
  • Follow Standard Conventions: Use a hierarchical naming scheme like CompanyName.ProductName.Feature to keep your codebase organized and intuitive.
  • Use global using: For commonly used namespaces (System, System.Linq, your project’s Models namespace), define them in a GlobalUsings.cs file to reduce boilerplate.
  • Avoid Type Pollution: Only import the namespaces you actually need. Don’t add a using directive if you aren’t using any types from that namespace.
  • Use Aliases for Clarity: When conflicts arise or type names are very long and generic, use an alias to improve code readability.