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.
System System.Collections.Generic System.IO System.Linq System.Net.Http System.Threading System.Threading.Tasks
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
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.
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?