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.

To download the source code for this article, you can visit our GitHub repository.

Let’s begin.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

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.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!