I'll be talking about the versatile using
directive in C# - its various usages, the subtle differences of placing it inside vs. outside of the namespace, and other related things. So, let's begin.
1. The Basic using
Directive
Before we go into the complexities, let's review the basics. The using
directive is primarily used for including namespaces, which helps avoid specifying the full namespace path every time you access a class or member.
using System;
using System.Collections.Generic;
// You can now use List<T> without specifying its full path.
List<int> numbers = new List<int>();
2. Inside vs. Outside Namespaces
You might've seen using
directives placed both inside and outside namespaces. Does the location matter? Absolutely!
- Outside Namespaces: The most common usage. It makes the specified namespaces available to all the code in the file.
- Inside Namespaces: This restricts the scope of the
using
directive to only that namespace. This can help avoid ambiguity if there are identically named types across different namespaces in the same file. For those curious, I personally lean towards this approach.
using System; // Global to the file
namespace A {
using System.Collections.Generic; // Specific to namespace A
...
}
namespace B {
// Doesn't have access to System.Collections.Generic from namespace A
...
}
This practice can be useful in larger projects where managing dependencies becomes crucial.
3. using
Aliases
In cases where there's a naming conflict or when you want to give a type a more meaningful or shorter name, you can use using
to create an alias:
using StringCollection = System.Collections.Specialized.StringCollection;
With this alias in place, you can use StringCollection
instead of its full namespace path.
4. Static using
Directive
C# 6 introduced the static using
directive, which allows you to access static members of a type without specifying the type name:
using static System.Math;
double radius = 10.0;
double area = PI * Pow(radius, 2); // No need to prefix with Math.
5. Using Directives for Additional Types in C# 12
New capabilities are coming for the using
directive in the upcoming version 12 of the C# language. Now, the using
directive has been extended to support almost any type. This means you're not restricted to just namespaces or specific classes anymore!
Examples:
using Measurement = (string, int);
using PathOfPoints = int[];
using DatabaseInt = int?;
Tuples: Particularly exciting is the support for tuples. You can now alias them with both element names and types.
using Measurement = (string Units, int Distance);
You can read more about it on the Microsoft dev blog. I don't see why you would do this instead of creating a separate class, but you have the option to use it this way!
Is this all you've got!?
Absolutely not!
The term using
in C# can be both a keyword and a directive, depending on its context:
- Directive: When used at the top of a C# file,
using
is a directive that allows you to import namespaces or create type aliases, e.g.,using System;
orusing StringCollection = System.Collections.Specialized.StringCollection;
. - Keyword: When used in the context of resource management, it's a keyword. It denotes a statement in C# that provides a convenient syntax to ensure that an object implementing
IDisposable
is correctly disposed of. Read more aboutIDisposable
, managed and unmanaged resources in the previous newsletter.
The using
statement for Resource Management
Before C# 8, the using
statement was widely used for resource management, especially for objects holding unmanaged resources. It made sure that the object's Dispose
method was called once the object was no longer needed.
Here's an example:
using (StreamWriter writer = new StreamWriter("file.txt"))
{
writer.Write("Hello, world!");
}
Under the hood
The using
statement translates to a try-finally
block. The previous example becomes something similar to:
StreamWriter writer = new StreamWriter("file.txt");
try
{
writer.Write("Hello, world!");
}
finally
{
writer?.Dispose();
}
This ensures that even if an exception occurs within the scope of the using
statement, the Dispose
method is always called during the cleanup in the finally
block.
C# 8's "using" declaration
The C# 8 introduced the using
declaration, a more succinct way to ensure the disposal of resources. The object will be disposed of at the end of the containing scope.
using var writer = new StreamWriter("file.txt");
writer.Write("Hello, world!");
// writer is disposed of at the end of the current scope.
Does it only work with IDisposable
?
Yes, the using
statement or declaration works specifically with classes that implement the IDisposable
interface. This interface contains just one method, Dispose
, which is meant to release all the resources, especially unmanaged resources. If you try to use the using
statement on a type that does not implement IDisposable
, you'll get a compile-time error telling you that "Type used in a using statement must be implicitly convertible to 'System.IDisposable'".
Quick Tips & Best Practices:
- Limit Alias Usage: While aliases can be handy, overusing them can make code harder to understand for others. Use them sparingly and only when necessary.
- Organize & Group: If you have multiple
using
directives, group them by purpose or library. This makes it easier to spot related dependencies. - Clean Up: Use tools like ReSharper or Visual Studio's built-in features to remove unnecessary
using
directives, keeping your codebase clean.
If you prefer to keep your using
directives within the namespace, as I do, but you don't have a ReSharper to do it automatically. Fear not! Since Visual Studio 2019 update 16.1, you can do so automatically with just Visual Studio without additional extensions. I shared this tip with the StackOverflow community a long time ago.
- Open Visual Studio
- In the toolbar, go to Tools
- Within the Tools menu, open Options
- In the options list, select Text Editor > C# > Code Style > General
- Scroll down near the bottom, find the 'using' preferences section, and set the Preferred 'using' directive placement to Inside namespace
And if you want to enforce the same thing through your .editorconfig
file, add the following in there:
csharp_using_directive_placement = inside_namespace:suggestion
Conclusion
The using
directive offers many utility features that can make your C# coding experience smoother and more efficient. Whether resolving naming conflicts, simplifying access to static members, or ensuring resources are cleaned up, using
is used a lot in the C# language.