Implicit Using Statements In .NET 6

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.

Implicit Global Usings

Implicit Global Usings are actually a hidden auto generated file, inside your obj folder, that declares global using statements behind the scenes. Again, this is only the case for .NET 6 and C# 10. In my case, if I go to my project folder then go obj/Debug/net6.0, I will find a file titled “RandomNumbers.ImplicitNamespaceImports.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 using .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?

The only way to turn off implicit using statements completely is to add the following line to your .csproj file :

<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>net6.0</TargetFramework>
  <LangVersion>preview</LangVersion>
  <DisableImplicitNamespaceImports>true</DisableImplicitNamespaceImports>
</PropertyGroup>

This turns off all implicit imports. However, there is also another option to selectively remove (and add) implicit namespaces like so :

<ItemGroup>
  <Import Remove="System.Threading" />
  <Import 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

I rarely comment on new features being good or bad. Mostly because I presume that people much smarter than me 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.

That being said, I don’t like the “hidden”-ness of everything. I’m almost certain that stackoverflow will be overwhelmed with people asking why their compiler is saying that System previously appeared in the namespace when it clearly didn’t. It’s not exactly intuitive to go into the obj folder and check what’s in there. In fact, I can’t think of a time I have in the past 15 years. Remember, if you upgrade an existing .NET 5 solution to .NET 6, you will automatically be opted in.

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.

4 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.

Leave a Reply

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