RefactorMCP catalog

Introduce Using Declaration

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.

Transformation

  • 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

CodeMeaning
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

·

Diffs show each file from before to after. /*[*/ … /*]*/ marks a selection and /*^*/ a caret; the runner removes them before the refactoring runs.

#await-using

An await using keeps await

success
TargetSample.cs, the /*^*/ caret
Sample.csmodified
11 using System;
22 using System.Threading.Tasks;
33
44 public sealed class Connection : IAsyncDisposable
55 {
66 public Task SendAsync(string message) => Task.CompletedTask;
77
88 public ValueTask DisposeAsync() => ValueTask.CompletedTask;
99 }
1010
1111 public class Sample
1212 {
1313 public async Task SendAsync(string message)
1414 {
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);
1917 }
2018 }

#embedded-statement

A using whose body is a single statement without braces

success
TargetSample.cs, the /*^*/ caret
Sample.csmodified
11 using System.IO;
22
33 public class Sample
44 {
55 public void Write(string path, string text)
66 {
7− /*^*/using (var writer = new StreamWriter(path))
8− writer.Write(text);
7+ using var writer = new StreamWriter(path);
8+ writer.Write(text);
99 }
1010 }

#embedded-statement-rejected

Refuses a using that is the body of an if without braces, where a declaration is not allowed

refusal
TargetSample.cs, the /*^*/ caret
Refusesnot-in-block; every file is left unchanged
Sample.csinput
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 }

#generic

A resource of a generic type is declared with the type as written

success
TargetSample.cs, the /*^*/ caret
Sample.csmodified
11 using System;
22
33 public sealed class Lease<T> : IDisposable
44 {
55 public Lease(T value) => Value = value;
66
77 public T Value { get; }
88
99 public void Dispose()
1010 {
1111 }
1212 }
1313
1414 public class Sample
1515 {
1616 public T Use<T>(T value)
1717 {
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;
2220 }
2321 }

#keeps-comments

Comments above the using and inside its block stay with the code

success
TargetSample.cs, the /*^*/ caret
Sample.csmodified
11 using System;
22 using System.IO;
33
44 public class Sample
55 {
66 public void Copy(string from, string to)
77 {
88 Console.WriteLine("Copying");
99
1010 // Both files stay open until the copy ends.
11− /*^*/using (var source = File.OpenRead(from)) // read side
12− {
13− // Truncates any existing file.
14− using var target = File.Create(to);
11+ using var source = File.OpenRead(from); // read side
12+ // Truncates any existing file.
13+ using var target = File.Create(to);
1514
16− source.CopyTo(target);
17− }
15+ source.CopyTo(target);
1816 }
1917 }

#language-version-rejected

Refuses a project on a language version before using declarations

refusal
TargetSample.cs, the /*^*/ caret
ProjectlangVersion 7.3
Refuseslanguage-version; every file is left unchanged
Sample.csinput
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 }

#multiple-declarators

A using that declares several resources declares them all

success
TargetSample.cs, the /*^*/ caret
Sample.csmodified
11 using System.IO;
22
33 public class Sample
44 {
55 public void Copy(string from, string to)
66 {
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);
119 }
1210 }

#name-conflict-rejected

Refuses a resource whose name another local earlier in the block already uses

refusal
TargetSample.cs, the /*^*/ caret
Refusesname-conflict; every file is left unchanged
Sample.csinput
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 }

#nested-block

A using that ends an inner block, such as an if's, is disposed at the end of that block as before

success
TargetSample.cs, the /*^*/ caret
Sample.csmodified
11 using System;
22 using System.IO;
33
44 public class Sample
55 {
66 public void Append(string path, string line, bool enabled)
77 {
88 if (enabled)
99 {
10− /*^*/using (var writer = File.AppendText(path))
11− {
12− writer.WriteLine(line);
13− }
10+ using var writer = File.AppendText(path);
11+ writer.WriteLine(line);
1412 }
1513
1614 Console.WriteLine("Done");
1715 }
1816 }

#nested-usings

Usings stacked without braces between them all become declarations

success
TargetSample.cs, the /*^*/ caret
Sample.csmodified
11 using System.IO;
22
33 public class Sample
44 {
55 public void Copy(string from, string to)
66 {
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);
1210 }
1311 }

#no-variable-rejected

Refuses a using of an expression, which declares no variable

refusal
TargetSample.cs, the /*^*/ caret
Refusesno-variable; every file is left unchanged
Sample.csinput
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 }

#not-a-using-statement-rejected

Refuses a caret that is not on a using statement

refusal
TargetSample.cs, the /*^*/ caret
Refusesnot-a-using-statement; every file is left unchanged
Sample.csinput
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 }

#nullable-context

In a nullable context a resource that may be null is declared as it was

success
TargetSample.cs, the /*^*/ caret
Projectnullable enable
Sample.csmodified
11 using System.IO;
22
33 public class Sample
44 {
55 public string? FirstLine(string? path)
66 {
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();
119 }
1210 }

#simple

A using statement that ends its block becomes a using declaration followed by its body

success
TargetSample.cs, the /*^*/ caret
Sample.csmodified
11 using System.IO;
22
33 public class Sample
44 {
55 public string Read(string path)
66 {
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();
1210 }
1311 }

#statements-after-rejected

Refuses a using followed by more statements, which would run before the resource is disposed

refusal
TargetSample.cs, the /*^*/ caret
Refusesnot-last-statement; every file is left unchanged
Sample.csinput
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 }

#switch-section-rejected

Refuses a using directly in a switch section, where a using declaration is not allowed

refusal
TargetSample.cs, the /*^*/ caret
Refusesin-switch-section; every file is left unchanged
Sample.csinput
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 }