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.
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.
Comments (4)
Great to have your blog posts back, they are absolutely great
Thank you so much Rene, means a lot to me. Have a great day, Thomas
Very interesting ! Thanks a lot
Thank you for the feedback, Alessandro