The “using” directive in C# helps us reduce boilerplate code when referencing types and members that sit outside the context of our class. C# 12 further expanded on the using directive, allowing it to be used for additional types.
In this article, we’ll start by looking at how “using” has been used so far in .NET. Then we’ll explore how to use the using directive with more types than in the past.
Let’s begin.
The Common Uses of the ‘using’ Directive
The using
directive has been around for several years and the C# team has expanded its capabilities throughout different language versions.
Let’s take a look at the ways we’ve been able to use it up until now.
Import External Namespaces
The most popular use of using
is to import the types of an external namespace.
Let’s import the namespace System.Collections.Generic
into our code so we can “use” the types and functionality that it includes:
using System.Collections.Generic;
Now we can use List<T>
as if it was a member of our class:
public static List<Article> GetArticles() { return new List<Article>() { new("How to Use the “Using Static” Feature in C#", 950), new("Global Using Directives in C#", 1100), new("Using Directive for Additional Types", 800) }; }
Importing namespaces via the using
directive is a fundamental part of writing code in C# and the language has included it since its first version.
Access Static Members
C# 6 introduced the using static
directive that aims to reduce the boilerplate code when accessing static members. It enables us to import the members of a static
class and then use them as if they were local.
Let’s import System.Math
so that we can use its members:
using static System.Math;
As a result, we can call the Min()
method without having to include the fully qualified name of System.Math.Min()
or Math.Min()
every time:
public static int GetMinimumInteger(int num1, int num2, int num3, int num4) { return Min(Min(Min(num1, num2), num3), num4); }
The using static
directive is a great addition to the C# language, so make sure to check out our article How to Use the “Using Static” Feature in C# for a deep dive.
Global and Implicit ‘using’ Directives
C# 10 introduced the concepts of ‘global’ and ‘implicit’ using
directives.
While using
allows us to import a namespace in a class when a need arises, the global using
directive allows us to import namespaces that will be used globally, across the entire application. After declaring the namespace globally, we can use it in any file.
Let’s see how we can declare a global namespace with global using
. For that, we create a separate file named GlobalUsings.cs
to store all global usings:
global using UsingDirectiveForAdditionalTypes.Models;
Here, we declare the UsingDirectiveForAdditionalTypes.Models
namespace globally so that we can use its members everywhere in our application.
ImplicitUsings
is a closely related feature, also introduced in C# 10. It instructs the compiler to automatically generate a GlobalUsings.cs
file that contains all the using
directives required for our application. The setting can be enabled or disabled in the .csproj
file of our applications.
Let’s see where we can edit the value of the ImplicitUsings
setting:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>disable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project>
As we see here, we have disabled the ImplicitUsings
setting.
Make sure to check our Global Using Directives in C# article on global
and implicit
for more in-depth learning of this great feature.
Type Alias
The using
directive can also be used for type alias. C# introduced this feature a while ago, allowing us to import a type and alias it so that we can refer to it with a short name.
Let’s see how type alias works:
using CodeMazeArticle = UsingDirectiveForAdditionalTypes.Models.Article;
Here, we define the alias CodeMazeArticle
and set it to represent the type UsingDirectiveForAdditionalTypes.Models.Article
. This way, we can use CodeMazeArticle
in our code in place of the fully qualified type:
public static List<CodeMazeArticle> GetCodeMazeArticles() { return new List<CodeMazeArticle>() { new("How to Use the “Using Static” Feature in C#", 950), new("Global Using Directives in C#", 1100), new("Using Directive for Additional Types", 800) }; }
So far, this worked for a limited set of built-in types, custom types, and namespaces.
Now let’s take a look at the newly supported types.
The ‘using’ Directive for Additional Types
C# 12 further extended the type alias feature, and now we can use the using
directive to alias almost any type.
Nullable Types
One of the new types with alias support is nullable value types:
using NullableInt = int?;
Here we utilize the using
directive to alias int?
as NullableInt
. We can then use NullableInt
in our code:
public static List<NullableInt> GetArticleIDs() { return [1, 2, 3, null, 5]; }
Array Types
The Array
is another type with which we can use the using
directive to create an alias. We can use it to alias both arrays of built-in types and arrays of our custom types:
using Titles = string[];
Here, we are defining an alias Titles
to refer to string[]
. In our code below, we are using Titles
when referring to an array of strings
:
public static Titles GetArticleTitles() { return AliasExamples.GetArticles() .Select(a => a.Title) .ToArray(); }
Tuples
Tuples are lightweight data structures for grouping elements that may vary by type. It is a convenient way to store multiple values without the need to define a custom type. As an example, the following tuple groups together a string
and an int
value: (string, int)
Tuples are flexible and powerful, but the community has, in some cases, rightfully criticized them for making the code more difficult to reason about. That’s why C# later introduced tuple aliases.
We can use the using
directive to create aliases for tuples, making our code more readable:
using Location = (int X, int Y);
Here we’ve defined a new Tuple(int X, int Y)
and named it Location
. Now let’s use the Location
tuple:
public static List<Location> GetLocationsOfInterest() { return [new Location(25, 3), new Location(12, 8), new Location(-2, 0)]; }
Notice how it gives contextual information as if it were a common C# contract. You can read more about tuples and aliases in our article Tuple Aliases in C#.
Global ‘using’ Directives for Additional Types
One of the most powerful using
directive features is that we can define aliased types globally. This means that in the same way that we globally import namespaces, we can also globally alias a type and refer to this type with the same alias throughout our application!
Let’s see the syntax of the global using
directive for a tuple:
global using Destination = (string Location, double Distance);
Here, we define the Destination
tuple which we can then use throughout our application.
In our code below, we are initializing a new tuple with the alias Destination
and using it as if it were any other C# object. What’s more, we are achieving that without needing to import any additional namespace in our code context:
var (Location, Distance) = new Destination("London", 2500.00); Console.WriteLine($"My destination is {Location} with distance {Distance}");
Limitations of the ‘using’ Directive for Type Aliasing
The using
directive has always been a useful tool for increasing the readability of our code, which has only been enhanced now with new support for additional types.
While most types are now supported, the nullable reference type is still not:
using NullableArticle = UsingDirectiveForAdditionalTypes.Models.Article?;
Here we’ve defined an alias for a nullable Article
type, but the compiler will display an error that the “using alias cannot be a nullable reference type”.
We should also keep in mind that overusing the using
directive, or defining aliases with names that already exist as .NET types, may confuse and mislead other developers, especially those with less experience. Imagine a scenario in which we define an alias Point
for a 3D array (int X, int Y, int Z)
. Point already exists as struct
in .NET and our alias would confuse developers over which of the two types we should be using.
For these reasons, we should be careful when introducing using
directives into our code.
Conclusion
In this article, we’ve learned how we can use the “using” directive with additional types.
We’ve shown how we’ve used the using directive so far in .NET, to import namespaces as well as reference and alias types. Then we continued by showing how, in C# 12, alias support via the using directive is expanded to even more .NET types.