NoUsingDeclaration
Summary
Use Using Declarations to declare a local variable whenever possible.
Default severity
Warning
Description
When you declare a local variable and initialize it with an
IDisposable
object, you should use the Using Declaration
[1] whenever possible as follows:
// For implicitly-typed variables
using var reader = new StreamReader("file.txt");
// For explicitly-typed variables
using StreamReader i = new("file.txt");
// Multiple declarators are allowed in explicit type declarations
using StreamReader j = new("1.txt"), k = new("2.txt");
The Using Declaration is preferred to the Using Statement or the try
-finally
idiom because it is easier to describe RAII in C#.
Remarks
This analyzer only triggers diagnostics if the local variable is declared and
initialized with the new
operator. It ignores the declaration of the local
variable that is initialized with an IDisposable
instance any function or
property returns, as follows:
// OK
var out = System.Console.Out;
static TextReader NewStreamReader() => new StreamReader("file.txt");
// OK
var reader = NewStreamReader();
Initialization with an IDisposable
instance created with an operator other
than the new
operator is also not covered as follows:
// OK (but you should prevent resource leaks)
var reader = (inputFile is null)
? new StringReader(defaultText)
: new StreamReader(inputFile);
Cases where no diagnosis is issued
This analyzer should not raise a diagnostic when a factory method creates and
returns an IDisposable
object as follows:
public Socket? NewTcpSocket(Uri uri, int port)
{
// XXX (you cannot use Using Declaration)
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
try
{
socket.Connect(uri.Host, port);
return socket;
}
catch (Exception)
{
socket.Dispose();
}
return null;
}
Similarly, this analyzer should not raise a diagnostic if you instantiate an
IDisposable
object and assign it to a field or property or capture it as
follows:
private StreamReader? streamReader;
private StreamWriter? SharedWriter { get; set; }
public void PrepareStream()
{
// XXX (you cannot use Using Declaration)
var reader = new StreamReader("input.txt");
streamReader = reader;
var writer = new StreamWriter("output.txt");
SharedWriter = writer;
⋮
}
public static Action WriteHelloAction()
{
// XXX (you cannot use Using Declaration)
var writer = new StreamWriter("file.txt");
return () =>
{
// How do you dispose of it!?
writer.WriteLine("hello");
};
}
This analyzer should still not raise a diagnostic in the following complex case
where you instantiate an IDisposable
object wrapped in a decorator pattern:
public BufferedStream NewClientStream(…)
{
// XXX (you cannot use Using Declaration)
var clientSocket = new Socket(
AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientSocket.Connect(…);
var netStream = new NetworkStream(clientSocket, true);
var bufStream = new BufferedStream(netStream, streamBufferSize);
return bufStream;
}
It also does not issue a diagnostic when you reassign any value to the variable as follows:
// XXX (Using Declaration causes an error CS1656 at the line /*💀*/.)
var i = new StreamReader("file.txt");
Console.WriteLine(i.ReadLine());
/*💀*/ i = new StreamReader("another.txt");
Console.WriteLine(i.ReadLine());
In summary, this analyzer will not issue diagnostics if the variable representing the created instance is:
- used as a parameter of a method or constructor
- on the right side of the assignment expression
- captured
- returned
- reassigned
Therefore, you should be aware that resource leaks can occur even though this analyzer does not issue any diagnostics.
Classes whose dispose() method does nothing
The local variables whose type is one of the following are not covered:
System.IO.StringReader
System.IO.StringWriter
System.IO.MemoryStream
System.IO.UnmanagedMemoryStream
System.IO.UnmanagedMemoryAccessor
See also UnnecessaryUsing analyzer.
For example:
// OK
var reader = new StringReader("hello");
// OK
StringReader i = new("hello");
However, even if the concrete class of the instance is one of those above, it is covered if the type of the variable is not so, as follows:
// NG
TextReader i = new StringReader("hello");
Code fix
The code fix introduces an option to add a using
keyword before var
or the
type name.
Example
Diagnostic
public sealed class Code
{
public static void Main()
{
var i = new StreamReader("file.txt");
StreamReader j = new("file.txt");
}
}
Code fix
public sealed class Code
{
public static void Main()
{
using var i = new StreamReader("file.txt");
using StreamReader j = new("file.txt");
}
}
References
[1] Microsoft, "pattern-based using" and "using declarations"