C# 10.0: Global Using Directives – Make Important Namespaces Available in Your Whole Project

In the previous blog post you learned about C# 10.0 file-scoped namespaces. In this blog post you learn about another C# 10.0 feature that is called global using directives.

C# 10.0 allows you to define using directives globally, so that you don’t have to write them in every file. Let’s take a look at a simple example.

Create a Global Using Directive

Let’s say you have created the following Employee class in a Console application, and you defined this Employee class in an Employee.cs file (Note also the nice file-scoped namespace in the code-snippet below. :-))

namespace EmployeeManager.Model;

public class Employee
{
  public string? FirstName { get; set; }
}

To use this class in the Program.cs file, you need to add a using directive for the EmployeeManager.Model namespace like you see it in the codesnippet below. Note that there is also a using directive for the System namespace that contains the Console class. Below you see a full Program.cs file. It uses C# 9.0 Top-Level Statements, so there’s no Main method. It also uses a C# 9.0 target-typed new expression to create the Employee instance. But that’s just C# sugar, we actually care in this blog post about the two using directives.

using System;
using EmployeeManager.Model;

Employee emp = new() { FirstName = "Thomas" };
Console.WriteLine(emp.FirstName);

With C# 10.0, you can get rid of the two using directives in the Program.cs file by defining them globally in any C# file of your project. To define a using directive globally, you just use the global keyword before the directive like you see it in the next code-snippet. This makes the using directive respectively the namespace of that global using directive automatically available in every C# file of your project.

While you can define global usings in any C# file, I would recommend you to create a separate C# file for them to keep things maintainable and easy to find. Let’s add to the Console app for example a GlobalUsings.cs file with the following two global using directives:

global using System;
global using EmployeeManager.Model;

This means now that these two namespaces are available in every C# file of the project. So, the full Program.cs file can now look like below. Note that no using directives are necessary anymore in the Program.cs file for the namespaces System and EmployeeManager.Model:

Employee emp = new() { FirstName = "Thomas" };
Console.WriteLine(emp.FirstName);

That’s great!

Global Usings at The Top of Your C# File

If you define normal using directives and global using directives in a single C# file, the global using directives must precede the normal using directives. As you can see in the sceenshot below, you’ll get an error if you don’t stick to that rule:

Use Static Usings Globally

With C# 6.0, Microsoft introduced the using static directive. It let’s you access the members of a static class without writing the static class in your code. A typical example is the System.Math class that has many static methods like Min, Max, Abs and Round. You can use the class and its static Min method like this:

var minValue = System.Math.Min(2, 8);

With a using static directive, your code can look like below and the Min method can be used directly.

using static System.Math;

var minValue = Min(2, 8);

With C# 10.0, you can define a using static directive also globally like below by writing the global keyword before the using directive:

global using static System.Math;

This means that in any file of your project, you can now use the static methods of the Math class without adding a using directive and without writing the Math type in your code:

var minValue = Min(2, 8);
var maxValue = Max(2, 8);

Visual Studio 2022 Refactorings

If you keep normal using directives in your C# files for namespaces that are declared already globally with global using directives, Visual Studio suggests you that you can remove them, as they are unnecessary.

Also if you write the same global using directive in different C# files of your project, your code still compiles, but also here Visual Studio suggests you to remove the global using directives until there is only a single global using directive left per namespace.

So, that’s it. It is a simple feature that makes your code more compact. It is especially useful for namespaces that you use in many C# files of your project. Now, let’s look how the new .NET 6.0 project templates use this C# 10.0 feature.

.NET 6.0 Projects And Implicit (Global) Usings

When you create a new Console App with .NET 6.0, the Program.cs file contains only this single statement:

Console.WriteLine("Hello, World!");

Hey, wait, the Console class is in the System namespace, but there is no using directive for that namespace. This means there must be a global using directive somewhere in the project for the System namespace. But when you look at the Solution Explorer, you can’t see any other C# file than the Program.cs file.

Mhm, something must be going on behind the scenes. In the code-snippet below you can see the .csproj file of that .NET 6.0 console app project. Do you see the ImplicitUsings element that is set to the value enable? That’s a new element, and that’s where the magic happens.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

When you remove the ImplicitUsings element from the .csproj file or when you set the value to disable, then you would have to add a using directive in the Program.cs file for the System namespace, so that you can use the Console class. But let’s keep the ImplicitUsings element enabled and let’s take a look at how this works.

When you click in the toolbar of the Solution Explorer on “Show All Files” (the third icon from the right in the screenshot below), you can see all the files from the file system, and not only those that are part of your project. There you can navigate like in the screenshot below into the folder obj\Debug\net6.0. There you can see a generated ConsoleApp.GlobalUsings.g.cs file. That file is generated because of the ImplicitUsings element in the .csproj file. The file is compiled together with your project, and it contains global using directives for common namespaces, like for example the System namespace.

In the codesnippet below, you can see the content of the generated ConsoleApp.GlobalUsings.g.cs file. So, there you see all the namespaces that are automatically available in every C# file of your .NET 6.0 console app.

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

Note: I understand that the additional use of global::System instead of just System in the code snippet above can be a bit confusing if you haven’t seen this before. But that’s nothing new in C# 10.0. That global::System avoids problems if there is an overlap because of user-defined namespaces. If I would have defined for example a namespace Thomas.System, a using global::System directive ensures in all cases that the System namespace of .NET is used and not mine. So, a using global::System directive is a more explicit way than just using System to refer to the System namespace of .NET, but it is still a normal using directive. To make both of them a global using directive, you have to write the global keyword before the using keyword like you see it in the code-snippet above.

Implicit Usings are Project Specific

In the previous section you saw the implicit using directives that are generated for a .NET console app. These implicitly created global using directives are project specific. For example, when you create a new ASP.NET Core Web App project with .NET 6.0, you find the following global using directives in the generated GlobalUsings.g.cs file:

// <auto-generated/>
global using global::Microsoft.AspNetCore.Builder;
global using global::Microsoft.AspNetCore.Hosting;
global using global::Microsoft.AspNetCore.Http;
global using global::Microsoft.AspNetCore.Routing;
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Net.Http.Json;
global using global::System.Threading;
global using global::System.Threading.Tasks;

Summary

Global using directives allow you to define the namespaces that you usually need in every file of your project in a single C# file. You also read that new .NET 6.0 projects generate global using directives implicitly for common .NET namespaces like for example System and System.Linq. That feature can be enabled/disabled in the .csproj file with the ImplicitUsings element. If you don’t specify that element, the feature is disabled.

I must say that I like global using directives, it’s a simple but great addition to the C# programming language.

In the next blog post, you will learn about extended property patterns in C# 10.0.

Share this post

Comments (3)

  • Mina Reply

    Hello Thomas
    wow It is amazing :)

    Regards,
    Mina

    September 30, 2021 at 3:51 pm
  • Alain Dekker Reply

    This is a kind of “polluting the global namespace because we can” kind of feature. In my opinion, its clever but messy and not wise. Using clauses, like variables and many other programming constructs, should be more focused.

    May 22, 2023 at 12:14 pm

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.