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.
Comments (3)
Hello Thomas
wow It is amazing :)
Regards,
Mina
Absolutely Mina, it is. :)
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.