C# 12: Alias Any Type

In the previous blog posts of this C# 12 series you learned about different C# 12 features:

In this blog post, let’s look at another new feature that allows you to alias any type with a using directive. Before we dive in, let’s ensure that you understand how you could already alias namespaces and types before C# 12.

Alias Namespaces and Types Before C# 12

Already since C# 1.0 you can alias namespaces and types. Let’s take a look at what’s possible before we look at the enhanced feature of C# 12.

Let’s take the following Person class and its namespace ThomasClaudiusHuber.Models as an example.

namespace ThomasClaudiusHuber.Models
{
    public class Person
    {
        public string? FirstName { get; set; }
    }
}

To use the Person class in another class that is not in the same namespace, you have different options. You can use the full qualified class name ThomasClaudiusHuber.Models.Person like in the code snippet below.

var person = new ThomasClaudiusHuber.Models.Person
{
    FirstName = "Thomas"
};
System.Console.WriteLine(person.FirstName);

Instead of the full qualified class name, you can also create a using directive to include the namespace ThomasClaudiusHuber.Models like in the code snippet below. The code snippet below is using top-level statements that were introduced with C# 9.0. But the using directive would be the same without top-level statements. So, I use in this blog post top-level statements to make everything a bit more compact.

using ThomasClaudiusHuber.Models;

var person = new Person
{
    FirstName = "Thomas"
};
System.Console.WriteLine(person.FirstName);

Since C# 1.0, you can alias a namespace. The following code snippet shows how the alias TchModels is created for the namespace ThomasClaudiusHuber.Models. In the code, the Person class can be referenced by using the alias TchModels.

using TchModels = ThomasClaudiusHuber.Models;

var person = new TchModels.Person
{
    FirstName = "Thomas"
};
System.Console.WriteLine(person.FirstName);

Instead of aliasing a namespace like in the code snippet above, you can also alias a type like the Person class. You can see this in the code snippet below. Note how the code is using the TchPerson alias to create a Person object.

using TchPerson = ThomasClaudiusHuber.Models.Person;

var person = new TchPerson
{
    FirstName = "Thomas"
};
System.Console.WriteLine(person.FirstName);

So, you see, there are namespace aliases and type aliases, both available since C# 1.0. Type aliases is exactly the feature that is enhanced with C# 12, so that they work not only with classes, but also with other types like tuples, arrays, and pointers. Before we look at this, let’s ensure that you understand why you should use aliases at all, and let’s also look at another feature of the using directive, the so-called static usings.

Why Should You Use Aliases for Namespaces and Types?

Sometimes you have the same class names in different namespaces. Of course, exactly that’s the reason why we have namespaces, to ensure that classes with the same name can be distinguished by their namespace. If you need to use two classes with the same name from different namespaces, you cannot work with normal using directives anymore, because then the compiler cannot know which class it should use. Let’s take a look at an example.

I create a brand-new .NET 8.0 Console App and I change the content of the .csproj file to what you can see in the code snippet below. I use net8.0-windows, I comment out the ImplicitUsings element, so that no using directives are generated behind the scenes, and I include Microsoft’s desktop UI frameworks WPF and Windows Forms by adding the two elements UseWPF and UseWindowsForms. Both frameworks can be used to build Windows desktop apps. They are part of .NET for Windows, which is what is specified in the TargetFramework element.

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

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0-windows</TargetFramework>
        <!--<ImplicitUsings>enable</ImplicitUsings>-->
        <Nullable>enable</Nullable>
        <UseWPF>true</UseWPF>
        <UseWindowsForms>true</UseWindowsForms>
    </PropertyGroup>

</Project>

When you have bigger applications, you might use both frameworks like defined in the .csproj file above. WPF is a bit newer and more modern – Visual Studio 2022 is for example a WPF application. Windows Forms is older. It is available since .NET Framework 1.0. But it is still quite popular for enterprise apps, as it has a fantastic visual designer that allows you to create user interfaces very fast, and there are many great third-party control providers out there. With the help of interop, you can use WPF components in a Windows Forms app, and you can also use Windows Forms components in a WPF app.

Now, with this .csproj file in place, let’s try to create in the Program.cs file a WPF Button and also a Windows Forms Button. To do this, you can of course use the full qualified class names like in the code snippet below.

var wpfButton = new System.Windows.Controls.Button();
var winFormsButton = new System.Windows.Forms.Button();

What you cannot do anymore is to include using directives for the two namespaces that contain the Button classes. The code of the following code snippet does not compile. Of course it does not compile, because how should the C# compiler know which Button class it should use?

using System.Windows.Controls; // WPF
using System.Windows.Forms; // WinForms

var wpfButton = new Button();
var winFormsButton = new Button();

There is no way for the C# compiler to find out which Button class it should use. So, it gives you the following error.

This means, to use the two Button classes, the full qualified class names like shown at the beginning of this section work. But as the namespaces are quite long, you might want to shorten them with namespace aliases. This will make your code more compact and more readable. An alias is especially useful when you instantiate multiple types from such a long namespace. The code snippet below creates the namespace aliases Wpf and WinForms and uses them to reference the corresponding Button class.

using Wpf = System.Windows.Controls;
using WinForms = System.Windows.Forms;

var wpfButton = new Wpf.Button();
var winFormsButton = new WinForms.Button();

If it’s really just about buttons, you can also alias the Button types directly instead of the namespace:

using WpfButton = System.Windows.Controls.Button;
using WinFormsButton = System.Windows.Forms.Button;

var wpfButton = new WpfButton();
var winFormsButton = new WinFormsButton();

So, you see, creating aliases for namespaces and types is especially useful when you have overlapping class names.

Hey, do type aliases also work with generic types?

Yes. You can create type aliases for generic types, like for example for a List<string>, as you can see in the code snippet below.

using StringList = System.Collections.Generic.List<string>;

var strings = new StringList { "C#", "TypeScript" };

Great, now you know how to alias namespaces and types. Before we look at what C# 12 brings to the table, let’s also ensure that you know the concept of static usings.

Working with Static Usings

The following statement shows how to use the Console class with the full qualified class name and how to call its static WriteLine method.

System.Console.WriteLine("C# is great!");

Instead of using the full qualified class name, you can also add, like in the code snippet below, a using directive for the System namespace that contains the Console class. The using directive for the System namespace is added automatically as a global using when ImplicitUsings are enabled in the .csproj file.

using System; // Not necessary when ImplicitUsings are enabled

Console.WriteLine("C# is great!");

Now, what you can do as well to make your code a bit more compact is to create a type alias for the System.Console class like like in the following code snippet.

using Con = System.Console;

Con.WriteLine("C# is great!");

Since C# 6.0, you can also create static usings. A static using points directly to a type, like in the following code snippet to the System.Console class. With that static using in place, all static members of the class can be used without using the class itself. As you can see in the code snippet below, the static WriteLine method of the Console class can now be used without specifying the Console class anymore.

using static System.Console;

WriteLine("C# is great!");

So, you see, static usings are another speciality of the using directive. Now, let’s take a look at the additions that you get with C# 12.

Alias Any Type in C# 12

With C# 12, Microsoft allows you to alias any type with a using directive. New supported types are for example tuples, arrays, and pointers. Let’s take a look at tuples and arrays.

Alias Tuples

Sometimes when you work with data, you might create a tuple to store some data, like you see it in the code snippet below . The syntax for tuples was introduced with C# 7.0. As you can see, you just put the values into parentheses to create a tuple, and for every value a numbered Item property is created.

var person = ("Thomas", "Huber");

Console.WriteLine($"Fullname: {person.Item1} {person.Item2}");

You can also use named arguments when creating a tuple like you see it in the following code snippet. Then the C# compiler creates properties with the argument names. This makes your code more readable at the places where you access the tuple’s values. I assume you agree that FirstName and LastName in the code snippet below are more readable than Item1 and Item2 in the code snippet above.

var person = (FirstName: "Thomas", LastName: "Huber");

Console.WriteLine($"Fullname: {person.FirstName} {person.LastName}");

Now, while working on projects, I often had for example a class that uses tuples internally to store and pass data between methods. Then you have always the thought: Should I just use a tuple here or should I explicitly create a type like class/record/struct to store the data? Creating an explicit type could make the code more readable, especially when you have tuples with many values. C# 12 has now an additional solution besides creating an explicit type: Creating an alias for the tuple.

The following code snippet has a using directive that creates the alias Person for a tuple that stores the two strings FirstName and LastName. In the code, a tuple is created by using that Person alias. It looks now pretty much as you would have a Person type.

using Person = (string FirstName, string LastName);

var person = new Person("Thomas", "Huber");

Console.WriteLine($"Fullname: {person.FirstName} {person.LastName}");

Creating an alias for a tuple does not work before C# 12. In the screenshot below I try it with C# 11, and you can see, I’m getting an error.

Aliases for tuples are not supported before C# 12.

Of course, you can use the alias at any place where you would use the tuple. For example also as a return type of a method like in the code snippet below. As you can see, the GetThomas method has the return type Person, which is actually the alias for a tuple.

using Person = (string FirstName, string LastName);

var person = GetThomas();

Console.WriteLine(person.FirstName);

static Person GetThomas()
{
    return ("Thomas", "Huber");

    // The following is valid as well
    // return new Person("Thomas", "Huber");
}

So, creating aliases for tuples can be a great alternative to creating an explicit type like a class/record/struct. Especially if that data structure is only needed in a single file, then a tuple alias can be a great approach.

Alias Arrays

Besides tuples, you can also alias arrays with C# 12. The code snippet below creates the Str alias for the type string[]. Then that Str alias is used to declare a names variable that is initialized with a collection expression.

using Str = string[];

Str names = ["Julia", "Anna", "Thomas"];

That’s it, simple and straight forward.

Know the Limitations

So, you can alias any type with C# 12. But there is a limitation. You cannot alias a nullable reference type like in the following statement.

using TchPerson = ThomasClaudiusHuber.Models.Person?;

When you do this, you get an error, as you can see in the screenshot below.

Summary

You learned in this blog post about the new functionality of the using directive that allows you to alias any type. When I look at the code bases I worked with in the past 5 years, I believe that this feature is especially useful with tuples.

I hope you enjoyed reading this blog post.

In the next blog post you will learn about Default Parameters in Lambda Expressions.

Share this post

Comments (4)

  • Rene Reply

    Great to have your blog posts back, they are absolutely great

    March 27, 2024 at 4:47 pm
  • Alessandro Casati Reply

    Very interesting ! Thanks a lot

    March 29, 2024 at 4:45 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.