Skip to main content

C# Global Using Directives

Introduction

When working on C# projects, you often need to include the same namespaces in multiple files. This creates redundancy and clutters your code with repetitive using directives. Starting from C# 10, the language introduced global using directives, a feature that allows you to declare using statements once and make them available throughout your entire project.

In this tutorial, we'll explore how global using directives work, their benefits, and how to implement them effectively in your C# projects.

Understanding Using Directives

Before diving into global using directives, let's briefly review standard using directives.

In C#, a using directive allows you to use types from a namespace without specifying the fully qualified name:

csharp
// Without using directive
System.Console.WriteLine("Hello, World!");

// With using directive
using System;
Console.WriteLine("Hello, World!");

Traditionally, these directives need to be added at the top of each file where you want to use the referenced namespaces.

What are Global Using Directives?

Global using directives extend the concept of using directives by allowing them to be applied at the project level rather than at the file level. Once declared, they become available to all files in the project without needing to be redeclared in each file.

Syntax for Global Using Directives

The syntax is straightforward - simply add the global keyword before the using statement:

csharp
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading.Tasks;

Where to Declare Global Using Directives

You can place global using directives in any .cs file in your project, but there are a few common approaches:

  1. Dedicated File: Create a dedicated file (e.g., GlobalUsings.cs) that contains only global using directives.

  2. Project File: Define them directly in your .csproj file.

  3. Program.cs: Place them at the top of your entry point file.

Let's explore each approach:

Option 1: Dedicated File

csharp
// GlobalUsings.cs
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.IO;
global using System.Threading.Tasks;

Option 2: Project File

xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Using Include="System" />
<Using Include="System.Collections.Generic" />
<Using Include="System.Linq" />
<Using Include="System.IO" />
<Using Include="System.Threading.Tasks" />
</ItemGroup>
</Project>

Example: Before and After Global Using Directives

Let's compare code with and without global using directives to see the difference:

Before: Multiple Files with Repeated Using Directives

csharp
// File1.cs
using System;
using System.Collections.Generic;
using System.Linq;

public class UserService
{
public List<string> GetUserNames()
{
return new List<string> { "Alice", "Bob", "Charlie" };
}
}
csharp
// File2.cs
using System;
using System.Collections.Generic;
using System.Linq;

public class ProductService
{
public List<string> GetProductNames()
{
return new List<string> { "Laptop", "Phone", "Tablet" };
}
}

After: Using Global Directives

csharp
// GlobalUsings.cs
global using System;
global using System.Collections.Generic;
global using System.Linq;
csharp
// File1.cs - No need for using directives
public class UserService
{
public List<string> GetUserNames()
{
return new List<string> { "Alice", "Bob", "Charlie" };
}
}
csharp
// File2.cs - No need for using directives
public class ProductService
{
public List<string> GetProductNames()
{
return new List<string> { "Laptop", "Phone", "Tablet" };
}
}

Practical Real-World Example

Let's create a more substantial example showing how global using directives can simplify a typical web API project:

Global Usings File

csharp
// GlobalUsings.cs
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading.Tasks;
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.EntityFrameworkCore;
global using MyProject.Models;
global using MyProject.Services;
global using MyProject.Data;

Controllers Without Local Using Directives

csharp
// UserController.cs - No local using directives needed
public class UserController : ControllerBase
{
private readonly ApplicationDbContext _context;

public UserController(ApplicationDbContext context)
{
_context = context;
}

[HttpGet]
public async Task<ActionResult<IEnumerable<User>>> GetUsers()
{
return await _context.Users.ToListAsync();
}

[HttpGet("{id}")]
public async Task<ActionResult<User>> GetUser(int id)
{
var user = await _context.Users.FindAsync(id);

if (user == null)
{
return NotFound();
}

return user;
}
}

Using Aliases with Global Using Directives

You can also use aliases with global using directives, which is especially useful for avoiding naming conflicts:

csharp
global using Console = System.Console;
global using JsonDocument = System.Text.Json.JsonDocument;
global using WinForms = System.Windows.Forms;

Now you can use these aliases throughout your project:

csharp
// Any file in the project
public class Logger
{
public void LogMessage(string message)
{
Console.WriteLine($"LOG: {message}");
}
}

Benefits of Global Using Directives

  1. Reduced Code Redundancy: Eliminates repetitive namespace declarations across files.

  2. Cleaner Code Files: Individual code files become shorter and more focused on their actual logic.

  3. Centralized Management: Easier to add, remove, or modify namespace references for the entire project.

  4. Consistency: Ensures consistent namespace availability across all project files.

Best Practices

  1. Be Selective: Don't make every possible namespace global. Focus on the ones used across many files.

  2. Group Related Namespaces: Organize global usings by functionality or library.

  3. Consider Using a Dedicated File: Keep all global usings in one place for better maintainability.

  4. Document Project-Specific Namespaces: Let team members know which namespaces are globally available.

  5. Avoid Conflicts: Be careful with types that have the same name in different namespaces.

Potential Drawbacks

  1. Reduced Clarity: It might be less obvious which namespaces a specific file depends on.

  2. Namespace Pollution: Could lead to unnecessary namespaces being available in files that don't need them.

  3. Increased Risk of Name Collisions: If you globally import namespaces with conflicting type names.

Summary

Global using directives in C# 10 and later provide a powerful way to reduce code redundancy and improve maintainability by centralizing namespace imports across your project. By adding the global keyword to your using directives, you can declare namespaces once and make them available throughout your entire codebase.

This feature is particularly valuable for larger projects where the same set of namespaces is consistently used across many files. However, like any tool, it should be used judiciously to maintain code clarity and avoid potential naming conflicts.

Further Resources and Exercises

Resources

Exercises

  1. Refactor a Project: Take an existing small C# project and refactor it to use global using directives. Compare the before and after code size and readability.

  2. Mixed Approach: Create a project where some namespaces are declared globally and others locally. Decide on a logical separation strategy.

  3. Global Using with Aliases: Practice creating aliases for commonly used but verbose types using global using directives.

  4. Project File Configuration: Configure global usings in the project file instead of a dedicated C# file and compare the approaches.

  5. Identify Candidates: Analyze a large C# project to identify which namespaces would be the best candidates for conversion to global usings based on frequency of use.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)