Implicit Using Statements In .NET 6

This post is part of a series on .NET 6 and C# 10 features. Use the following links to navigate to other articles in the series and build up your .NET 6/C# 10 knowledge! While the articles are seperated into .NET 6 and C# 10 changes, these days the lines are very blurred so don’t read too much into it.

.NET 6

Minimal API Framework
DateOnly and TimeOnly Types
LINQ OrDefault Enhancements
Implicit Using Statements
IEnumerable Chunk
SOCKS Proxy Support
Priority Queue
MaxBy/MinBy

C# 10

Global Using Statements
File Scoped Namespaces


In a previous post, we talked about the coming ability to use global using statements in C# 10. The main benefit being that you were now able to avoid the clutter of declaring namespaces over and over (Things like using System etc) in every single file. I personally think it’s a great feature!

So it only makes sense that when you create a new .NET 6 project, that global usings are implemented right off the bat. After all, if you create a new web project, there are many many files auto generated as part of the template that will call upon things like System or System.IO, and it makes sense to just use GlobalUsings straight away from the start right?

Well… .NET 6 have solved the problem in a different way. With Implicit Using statements, your code will have almost invisible using statements declared globally! Let’s take a look at this new feature, and how it works.

Getting Setup With .NET 6 Preview

At the time of writing, .NET 6 is in preview, and is not currently available in general release. That doesn’t mean it’s hard to set up, it just means that generally you’re not going to have it already installed on your machine if you haven’t already been playing with some of the latest fandangle features.

To get set up using .NET 6, you can go and read out guide here : https://dotnetcoretutorials.com/2021/03/13/getting-setup-with-net-6-preview/

Remember, this feature is *only* available in .NET 6. Not .NET 5, not .NET Core 3.1, or any other version you can think of. 6.

Additionally, there was a change to this feature between .NET 6 Preview and .NET 6 RC. This article has been updated as of 2021-10-07 to reflect what is currently in the very latest SDK version. If you are unsure what you have, go grab the latest SDK just to be sure.

Implicit Global Usings

Implicit Global Usings are an opt in feature (kinda), that is new to .NET 6/C# 10. For existing projects that you are upgrading to .NET 6, you will need to add the following to your csproj file :

<ImplicitUsings>enable</ImplicitUsings>

However if you create a new project inside Visual Studio 2022 or using the latest SDK from the command line, this flag has already been enabled for you! So again, it’s somewhat opt in, it’s just that you will be opted in by default when creating a new project.

When enabled, implicit usings are actually a hidden auto generated file, inside your obj folder, that declares global using statements behind the scenes. In my case, if I go to my project folder then go obj/Debug/net6.0, I will find a file titled “ProjectName.GlobalUsings.g.cs”.

Opening this file, I can see it contains the following :

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 the fact this is an auto generated file, and we can’t actually edit it here. But we can see that it declares a whole heap of global using statements for us.

The project I am demoing this from is actually a console application, but each main project SDK type has their own global imports.

Console/Library

System
System.Collections.Generic
System.IO
System.Linq
System.Net.Http
System.Threading
System.Threading.Tasks

Web

In addition to the console/library namespaces :

System.Net.Http.Json
Microsoft.AspNetCore.Builder
Microsoft.AspNetCore.Hosting
Microsoft.AspNetCore.Http
Microsoft.AspNetCore.Routing
Microsoft.Extensions.Configuration
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Hosting
Microsoft.Extensions.Logging

Worker

In addition to the console/library namespaces :

Microsoft.Extensions.Configuration
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Hosting
Microsoft.Extensions.Logging

Of course, if you are unsure, you can always quickly create a project of a certain type and check the obj folder for what’s inside.

If we try and import a namespace that previously appeared in our implicit global usings, we will get the usual warning.

Opting Out

As previously mentioned, if you are creating a brand new .NET 6 and C# 10 (Which in a years time, the majority will be), then this feature is turned on by default. I have my own thoughts on that, but what if you want to turn this off? This might be especially common when an automatically imported namespace has a type that conflicts with a type you yourself are wanting to declare. Or what if you just don’t like the hidden magic full stop?

Well of course we can either delete the flag all together, or set it to be disabled if we want to be a bit more explicit :

<ImplicitUsings>disable</ImplicitUsings>

Note that clearly, if we switch this flag to disabled on a live project that’s been going for some time, you’re likely to get 100’s of errors due to a project not importing the correct namespaces.

There is also another option to selectively remove (and add) implicit namespaces like so :

<ItemGroup>
  <Using Remove="System.Threading" />
  <Using Include="Microsoft.Extensions.Logging" />
</ItemGroup>

Here we are removing System.Threading and adding Microsoft.Extensions.Logging to the global implicit using imports.

This can also be used as an alternative to using something like a GlobalUsings.cs file in your project of course, but it is a somewhat “hidden” feature.

Is This A Good Feature? / My Thoughts

In the original version of this article, I was mostly down on this feature. And that’s saying something because I rarely comment on new features being good or bad. Mostly it’s because I presume that people much smarter than I know what they are doing, and I’ll get used to it. I’m sure when things like generics, lambdas, async/await got introduced, I would have been saying “I don’t get this”.

On the surface, I like the idea of project types having some sort of implicit namespace. Even imports that I thought were kinda dumb to be global such as “System.Threading.Tasks”, I soon realized are needed in every single file if you are using async/await since your methods must return a type of Task.

Originally, Implicit Usings were enabled by default *without* a flag in the csproj, and instead you had to add something like so to disable them :

<DisableImplicitNamespaceImports>true</DisableImplicitNamespaceImports>

The new version is better, in that we are opted in explicitly, but still the “hiddenness” of it all isn’t so great. I don’t like the idea of files being auto generated in the obj folder because for the most part, people do not go and look there. If you are doing a Ctrl + Shift + F inside your project to find a pesky namespace clash, you’re not going to find your auto generated implicit usings file.

I feel like a simpler idea would have been to edit the Visual Studio/Command Line templates that when you create a new web project, it automatically creates a GlobalUsings.cs file in the root of the project with the covered implicit namespaces already in there. To me, that would be a heck of a lot more visible, and wouldn’t lead to so much confusion over where these hidden imports were coming from.

That being said, maybe in a years time we just get used to it and it’s just “part of .NET” like so many other things. What’s your thoughts?

ENJOY THIS POST?
Join over 3.000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

6 comments

    1. Really? That makes things better IMO (Although.. Again, I’d personally prefer the GlobalUsings.cs approach and make it opt-in by default, but then atleast the file is visible and you know what’s going on)

  1. Did they remove this “feature”? Can somebody confirm? I think your blog should be more popular and people should not introduce random features to C#. I like idea about reducing imports but I do not like idea about making C# mess.

    I hope they will not add it and instead they will focus on PERFORMANCE to outplay Java or other high performance languages and frameworks.

    1. It is still available in the current release candidate (SDK v6.0.100-rc.1.21463.6) but project file tag names have changed.

      Now by default the project will include `enable` to turn the feature on.

      Selectively remove (and add) implicit namespaces no longer use `Import`, they use `Using`:

      Other than that, it seems to work as advertised.

      Commenting mostly because it took me a while to find the updated tag names to control the individual `Remove` and `Include` and hope this helps someone shorten their search.

  2. Thanks for the comments guys, RC1 did indeed change many of the flags and how you opt in to this feature. I’ve updated the article to reflect the changes.

Leave a Reply

Your email address will not be published. Required fields are marked *