Turns a using statement whose block runs to the end of its enclosing block into a C# 8 using declaration followed by the block's statements:
using (var reader = new StreamReader (path))
{
return reader.ReadToEnd ();
}
becomes
using var reader = new StreamReader (path);
return reader.ReadToEnd ();
Target
The using statement, by a caret anywhere in it. Usings stacked without braces between them, using (a) using (b) { ... }, are converted together.
Precondition
The project's language version is C# 8 or later. The using declares its resources, using (var x = ...), rather than disposing an expression. It is the last statement of a block, so the resources are disposed at the same point afterwards. It is not directly in a switch section, nor the body of another statement without braces, where a declaration is not allowed. Neither the resources nor the locals declared directly in its block share a name with a local declared elsewhere in the enclosing block, whose scope they would now overlap.
Each using (declaration) becomes using declaration;, keeping await and the declaration's type and every declarator. The statements of the innermost block follow, moved one level out.
Preserved
Behaviour: the resources are disposed at the end of the enclosing block, in reverse order, as before. Comments above the using, after its header and inside its block.
Limitations
A using statement followed by other statements is refused rather than wrapped in a block of its own. Comments on the block's own braces are not kept. The refactoring changes one statement, so there are no references in other files to update.
Error codes
Code Meaning not-a-using-statementthe caret is not on a using statement no-variablethe using disposes an expression and declares no variable not-last-statementstatements follow the using in its block and would run before disposal in-switch-sectionthe using is directly in a switch section not-in-blockthe using is the body of another statement without braces name-conflicta moved name is already declared elsewhere in the enclosing block language-versionthe project's language version is before C# 8
Cases
Expand all · Collapse all
Diffs show each file from before to after .
/*[*/ … /*]*/ marks a selection and /*^*/ a caret; the runner removes them before the refactoring runs.
# await-usingAn await using keeps await
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System ;2 2 using System .Threading .Tasks ;3 3 4 4 public sealed class Connection : IAsyncDisposable 5 5 { 6 6 public Task SendAsync (string message) => Task .CompletedTask ; 7 7 8 8 public ValueTask DisposeAsync () => ValueTask .CompletedTask ; 9 9 } 10 10 11 11 public class Sample 12 12 { 13 13 public async Task SendAsync (string message) 14 14 { 15 − /*^*/ await using (var connection = new Connection ()) 16 − { 17 − await connection.SendAsync (message); 18 − } 15 + await using var connection = new Connection (); 16 + await connection.SendAsync (message); 19 17 } 20 18 }
View fixture on GitHub
# embedded-statementA using whose body is a single statement without braces
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System .IO ;2 2 3 3 public class Sample 4 4 { 5 5 public void Write (string path, string text) 6 6 { 7 − /*^*/ using (var writer = new StreamWriter (path)) 8 − writer.Write (text); 7 + using var writer = new StreamWriter (path); 8 + writer.Write (text); 9 9 } 10 10 }
View fixture on GitHub
# embedded-statement-rejectedRefuses a using that is the body of an if without braces, where a declaration is not allowed
refusal
Target Sample.cs, the /*^*/ caret
Refuses not-in-block; every file is left unchanged
Sample.cs input
1 using System .IO ;2 3 public class Sample 4 { 5 public void Write (string path, bool enabled) 6 { 7 if (enabled) 8 /*^*/ using (var writer = new StreamWriter (path)) 9 { 10 writer.Write ("text" ); 11 } 12 } 13 }
View fixture on GitHub
# genericA resource of a generic type is declared with the type as written
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System ;2 2 3 3 public sealed class Lease <T > : IDisposable 4 4 { 5 5 public Lease (T value ) => Value = value ; 6 6 7 7 public T Value { get ; } 8 8 9 9 public void Dispose () 10 10 { 11 11 } 12 12 } 13 13 14 14 public class Sample 15 15 { 16 16 public T Use <T >(T value ) 17 17 { 18 − /*^*/ using (Lease <T > lease = new Lease <T >(value )) 19 − { 20 − return lease.Value ; 21 − } 18 + using Lease <T > lease = new Lease <T >(value ); 19 + return lease.Value ; 22 20 } 23 21 }
View fixture on GitHub
# language-version-rejectedRefuses a project on a language version before using declarations
refusal
Target Sample.cs, the /*^*/ caret
Project langVersion 7.3
Refuses language-version; every file is left unchanged
Sample.cs input
1 using System .IO ;2 3 public class Sample 4 { 5 public string Read (string path) 6 { 7 /*^*/ using (var reader = new StreamReader (path)) 8 { 9 return reader.ReadToEnd (); 10 } 11 } 12 }
View fixture on GitHub
# multiple-declaratorsA using that declares several resources declares them all
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System .IO ;2 2 3 3 public class Sample 4 4 { 5 5 public void Copy (string from, string to) 6 6 { 7 − /*^*/ using (FileStream source = File .OpenRead (from), target = File .Create (to)) 8 − { 9 − source.CopyTo (target); 10 − } 7 + using FileStream source = File .OpenRead (from), target = File .Create (to); 8 + source.CopyTo (target); 11 9 } 12 10 }
View fixture on GitHub
# name-conflict-rejectedRefuses a resource whose name another local earlier in the block already uses
refusal
Target Sample.cs, the /*^*/ caret
Refuses name-conflict; every file is left unchanged
Sample.cs input
1 using System ;2 using System .IO ;3 4 public class Sample 5 { 6 public void Write (string path, bool log) 7 { 8 if (log) 9 { 10 var writer = Console .Out ; 11 writer.WriteLine ("Writing" ); 12 } 13 14 /*^*/ using (var writer = new StreamWriter (path)) 15 { 16 writer.Write ("text" ); 17 } 18 } 19 }
View fixture on GitHub
# nested-blockA using that ends an inner block, such as an if's, is disposed at the end of that block as before
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System ;2 2 using System .IO ;3 3 4 4 public class Sample 5 5 { 6 6 public void Append (string path, string line, bool enabled) 7 7 { 8 8 if (enabled) 9 9 { 10 − /*^*/ using (var writer = File .AppendText (path)) 11 − { 12 − writer.WriteLine (line); 13 − } 10 + using var writer = File .AppendText (path); 11 + writer.WriteLine (line); 14 12 } 15 13 16 14 Console .WriteLine ("Done" ); 17 15 } 18 16 }
View fixture on GitHub
# nested-usingsUsings stacked without braces between them all become declarations
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System .IO ;2 2 3 3 public class Sample 4 4 { 5 5 public void Copy (string from, string to) 6 6 { 7 − /*^*/ using (var source = File .OpenRead (from)) 8 − using (var target = File .Create (to)) 9 − { 10 − source.CopyTo (target); 11 − } 7 + using var source = File .OpenRead (from); 8 + using var target = File .Create (to); 9 + source.CopyTo (target); 12 10 } 13 11 }
View fixture on GitHub
# no-variable-rejectedRefuses a using of an expression, which declares no variable
refusal
Target Sample.cs, the /*^*/ caret
Refuses no-variable; every file is left unchanged
Sample.cs input
1 using System .IO ;2 3 public class Sample 4 { 5 public void Touch (string path) 6 { 7 /*^*/ using (File .Create (path)) 8 { 9 } 10 } 11 }
View fixture on GitHub
# not-a-using-statement-rejectedRefuses a caret that is not on a using statement
refusal
Target Sample.cs, the /*^*/ caret
Refuses not-a-using-statement; every file is left unchanged
Sample.cs input
1 using System .IO ;2 3 public class Sample 4 { 5 public string Read (string path) 6 { 7 /*^*/ var reader = new StreamReader (path); 8 return reader.ReadToEnd (); 9 } 10 }
View fixture on GitHub
# nullable-contextIn a nullable context a resource that may be null is declared as it was
success
Target Sample.cs, the /*^*/ caret
Project nullable enable
Sample.cs modified
1 1 using System .IO ;2 2 3 3 public class Sample 4 4 { 5 5 public string ? FirstLine (string ? path) 6 6 { 7 − /*^*/ using (StreamReader ? reader = path is null ? null : new StreamReader (path)) 8 − { 9 − return reader?.ReadLine (); 10 − } 7 + using StreamReader ? reader = path is null ? null : new StreamReader (path); 8 + return reader?.ReadLine (); 11 9 } 12 10 }
View fixture on GitHub
# simpleA using statement that ends its block becomes a using declaration followed by its body
success
Target Sample.cs, the /*^*/ caret
Sample.cs modified
1 1 using System .IO ;2 2 3 3 public class Sample 4 4 { 5 5 public string Read (string path) 6 6 { 7 − /*^*/ using (var reader = new StreamReader (path)) 8 − { 9 − var text = reader.ReadToEnd (); 10 − return text.Trim (); 11 − } 7 + using var reader = new StreamReader (path); 8 + var text = reader.ReadToEnd (); 9 + return text.Trim (); 12 10 } 13 11 }
View fixture on GitHub
# statements-after-rejectedRefuses a using followed by more statements, which would run before the resource is disposed
refusal
Target Sample.cs, the /*^*/ caret
Refuses not-last-statement; every file is left unchanged
Sample.cs input
1 using System ;2 using System .IO ;3 4 public class Sample 5 { 6 public void Write (string path, string text) 7 { 8 /*^*/ using (var writer = new StreamWriter (path)) 9 { 10 writer.Write (text); 11 } 12 13 Console .WriteLine (File .ReadAllText (path)); 14 } 15 }
View fixture on GitHub
# switch-section-rejectedRefuses a using directly in a switch section, where a using declaration is not allowed
refusal
Target Sample.cs, the /*^*/ caret
Refuses in-switch-section; every file is left unchanged
Sample.cs input
1 using System .IO ;2 3 public class Sample 4 { 5 public void Write (int kind, string path) 6 { 7 switch (kind) 8 { 9 case 1 : 10 /*^*/ using (var writer = new StreamWriter (path)) 11 { 12 writer.Write ("one" ); 13 } 14 break ; 15 } 16 } 17 }
View fixture on GitHub