RefactorMCP catalog

Convert to Block Body

Replaces an expression body with a block holding the equivalent statement. The reverse of Convert to Expression Body.

Target

The declaration, by symbol: a method, constructor, operator, property, indexer or accessor (M:Sample.set_Name(System.String) for a setter). A local function, which has no symbol id, by a caret on its name.

Precondition

  • The declaration has an expression body.

Transformation

  • When the declaration returns a value, => expression; becomes { return expression; }: methods and local functions with a return type, operators, conversions and get accessors.
  • When it does not, the expression becomes a statement of its own: void methods, constructors, finalizers, set, init, add and remove accessors, and async methods returning Task or ValueTask, which await rather than return.
  • => throw expression; becomes { throw expression; }.
  • An expression-bodied property or indexer gets an accessor list with a get accessor holding the block.
  • The block is laid out on lines of its own. An expression spanning several lines keeps its line breaks, shifted to sit under the statement.

Preserved

  • Behaviour: the block means exactly what the expression body did.
  • Comments above the declaration stay there; a comment after the semicolon stays at the end of the statement.

Limitations

  • An auto-property's accessors have no body to expand, and an abstract member none at all; both are refused.

Error codes

CodeMeaning
already-block-bodythe declaration already has a block body
no-bodythe declaration has no body, such as an abstract method or an auto-property
not-a-member-with-bodythe target is not a method, property, accessor, constructor, operator or local function

Cases

·

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

#accessor

One expression-bodied accessor gets a block; the other accessor is left alone

success
Targetsymbol M:Sample.set_Name(System.String)
Sample.csmodified
11 public class Sample
22 {
33 private string _name = "";
44
55 public string Name
66 {
77 get => _name;
8− set => _name = value.Trim();
8+ set
9+ {
10+ _name = value.Trim();
11+ }
912 }
1013 }

#already-block-body-rejected

Refuses a member that already has a block body

refusal
Targetsymbol M:Sample.Total
Refusesalready-block-body; every file is left unchanged
Sample.csinput
1 public class Sample
2 {
3 private int _a;
4
5 public int Total()
6 {
7 return _a;
8 }
9 }

#async-methods

An async method returning Task awaits in a statement rather than returning the awaited value

success
Targetsymbol M:Sample.SaveAsync
Sample.csmodified
11 using System.Threading.Tasks;
22
33 public class Sample
44 {
5− public async Task SaveAsync() => await Task.Delay(10);
5+ public async Task SaveAsync()
6+ {
7+ await Task.Delay(10);
8+ }
69
710 public async Task<int> CountAsync() => await Task.FromResult(3);
811 }

#async-returning-value

An async method returning Task<T> returns the awaited value

success
Targetsymbol M:Sample.CountAsync
Sample.csmodified
11 using System.Threading.Tasks;
22
33 public class Sample
44 {
5− public async Task<int> CountAsync() => await Task.FromResult(3);
5+ public async Task<int> CountAsync()
6+ {
7+ return await Task.FromResult(3);
8+ }
69 }

#auto-property-rejected

Refuses an auto-property, whose accessors have no bodies to expand

refusal
Targetsymbol P:Sample.Name
Refusesno-body; every file is left unchanged
Sample.csinput
1 public class Sample
2 {
3 public string Name { get; set; } = "";
4 }

#constructor

An expression-bodied constructor with an initializer gets a block

success
Targetsymbol M:Shop.Order.#ctor(System.Int32)
Order.csmodified
11 namespace Shop;
22
33 public class Entity
44 {
55 }
66
77 public class Order : Entity
88 {
99 private readonly int _quantity;
1010
11− public Order(int quantity) : base() => _quantity = quantity;
11+ public Order(int quantity) : base()
12+ {
13+ _quantity = quantity;
14+ }
1215 }

#indexer

An expression-bodied indexer becomes an indexer with a get accessor

success
Targetsymbol P:Sample.Item(System.Int32)
Sample.csmodified
11 public class Sample
22 {
33 private readonly int[] _items = new int[4];
44
5− public int this[int index] => _items[index];
5+ public int this[int index]
6+ {
7+ get
8+ {
9+ return _items[index];
10+ }
11+ }
612 }

#keeps-comments

The comment above the member stays there and a comment after it stays with the statement

success
Targetsymbol M:Sample.Total
Sample.csmodified
11 public class Sample
22 {
33 private int _a;
44 private int _b;
55
66 /// <summary>The sum of both parts.</summary>
7− public int Total() => _a + _b; // unchecked
7+ public int Total()
8+ {
9+ return _a + _b; // unchecked
10+ }
811
912 public int Difference() => _a - _b;
1013 }

#local-function

An expression-bodied local function, targeted by a caret on its name, gets a block

success
TargetSample.cs, the /*^*/ caret
Sample.csmodified
11 public class Sample
22 {
33 public int Total(int[] prices)
44 {
55 var sum = 0;
66 foreach (var price in prices)
77 sum += WithTax(price);
88 return sum;
99
10− static int /*^*/WithTax(int price) => price * 120 / 100;
10+ static int WithTax(int price)
11+ {
12+ return price * 120 / 100;
13+ }
1114 }
1215 }

#multi-line-expression

An expression spanning several lines keeps its line breaks, indented under the return

success
Targetsymbol M:Sample.Total(System.Collections.Generic.List{System.Int32})
Sample.csmodified
11 using System.Collections.Generic;
22 using System.Linq;
33
44 public class Sample
55 {
6− public int Total(List<int> items) => items
7− .Where(i => i > 0)
8− .Sum();
6+ public int Total(List<int> items)
7+ {
8+ return items
9+ .Where(i => i > 0)
10+ .Sum();
11+ }
912 }

#no-body-rejected

Refuses an abstract method, which has no body

refusal
Targetsymbol M:Shape.Area
Refusesno-body; every file is left unchanged
Shape.csinput
1 public abstract class Shape
2 {
3 public abstract double Area();
4 }

#not-a-member-rejected

Refuses a field, which has no body

refusal
Targetsymbol F:Sample._total
Refusesnot-a-member-with-body; every file is left unchanged
Sample.csinput
1 public class Sample
2 {
3 private int _total;
4
5 public int Total => _total;
6 }

#nullable-context

A method returning a nullable reference converts without new warnings

success
Targetsymbol M:Sample.Find(System.String)
Projectnullable enable
Sample.csmodified
11 using System.Collections.Generic;
22
33 public class Sample
44 {
55 private readonly Dictionary<string, string> _names = new();
66
7− public string? Find(string key) => _names.TryGetValue(key, out var name) ? name : null;
7+ public string? Find(string key)
8+ {
9+ return _names.TryGetValue(key, out var name) ? name : null;
10+ }
811 }

#operator

An expression-bodied operator gets a block with a single return

success
Targetsymbol M:Shop.Money.op_Addition(Shop.Money,Shop.Money)
Money.csmodified
11 namespace Shop;
22
33 public readonly struct Money
44 {
55 public Money(decimal amount) => Amount = amount;
66
77 public decimal Amount { get; }
88
9− public static Money operator +(Money left, Money right) => new Money(left.Amount + right.Amount);
9+ public static Money operator +(Money left, Money right)
10+ {
11+ return new Money(left.Amount + right.Amount);
12+ }
1013 }

#read-only-property

An expression-bodied property becomes a property with a get accessor

success
Targetsymbol P:Sample.Name
Sample.csmodified
11 public class Sample
22 {
33 private string _first = "";
44 private string _last = "";
55
6− public string Name => _first + " " + _last;
6+ public string Name
7+ {
8+ get
9+ {
10+ return _first + " " + _last;
11+ }
12+ }
713 }

#return-statement

An expression-bodied method returning a value gets a block with a single return

success
Targetsymbol M:Sample.Total
Sample.csmodified
11 public class Sample
22 {
33 private int _a;
44 private int _b;
55
6− public int Total() => _a + _b;
6+ public int Total()
7+ {
8+ return _a + _b;
9+ }
710 }

#throw-expression

A throw expression becomes a throw statement

success
Targetsymbol M:Sample.Next
Sample.csmodified
11 using System;
22
33 public class Sample
44 {
5− public int Next() => throw new NotSupportedException();
5+ public int Next()
6+ {
7+ throw new NotSupportedException();
8+ }
69 }

#void-method

An expression-bodied void method gets a block with an expression statement

success
Targetsymbol M:Sample.Log(System.String)
Sample.csmodified
11 using System;
22
33 public class Sample
44 {
5− public void Log(string message) => Console.WriteLine(message);
5+ public void Log(string message)
6+ {
7+ Console.WriteLine(message);
8+ }
69 }