RefactorMCP catalog

Convert to Auto-Property

Replaces a property whose accessors only read and write a private field with an auto-property, and removes the field.

Precondition

  • The property's getter only returns a field, as _f or this._f, whether written as get { return _f; }, get => _f; or an expression body.
  • Each set or init accessor only assigns value to that same field.
  • The field is private, declared in the same type, of the same type and equally static.
  • Nothing passes the field by ref, out or in.
  • The property is not already an auto-property.

Transformation

  • The accessors lose their bodies and sit on one line, { get; set; }, keeping their modifiers and attributes.
  • The field's initialiser becomes the property's initialiser.
  • Other uses of the field, in any part of a partial type, use the property instead; this._f becomes this.Name.
  • A get-only property keeps no setter when only constructors write the field, since constructors may assign a get-only auto-property. When the type writes the field anywhere else, the property gets private set.
  • The field is removed with its comments; the property keeps its own.

Preserved

  • Every read and write of the value, and the property's accessibility and accessor modifiers.
  • Nullable annotations, including on a property of a type parameter.

Limitations

  • Comments inside the accessor bodies are removed with the bodies.
  • Assignments in constructors run through the property rather than the field, which is observable only if a derived type overrides the property.

Error codes

CodeMeaning
no-backing-fieldthe property is already an auto-property
not-trivialan accessor does more than read or write a single field of the property's type
backing-field-not-privatethe field is visible outside the type
field-passed-by-referencethe field is passed by reference

Cases

·

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

#backing-field-not-private-rejected

Refuses when the backing field is visible outside the type, where removing it would break its users

refusal
Targetsymbol P:Shop.Product.Price
Refusesbacking-field-not-private; every file is left unchanged
Product.csinput
1 namespace Shop
2 {
3 public class Product
4 {
5 protected decimal _price;
6
7 public decimal Price
8 {
9 get => _price;
10 set => _price = value;
11 }
12 }
13
14 public class Book : Product
15 {
16 public void Discount() => _price *= 0.9m;
17 }
18 }

#block-accessors

A property whose accessors only read and write a private field becomes an auto-property; the field is removed and its other uses read the property

success
Targetsymbol P:Shop.Basket.Count
Basket.csmodified
11 namespace Shop
22 {
33 public class Basket
44 {
5− private int _count;
5+ public int Count { get; set; }
66
7− public int Count
8− {
9− get { return _count; }
10− set { _count = value; }
11− }
12−
13− public void Clear() => this._count = 0;
7+ public void Clear() => this.Count = 0;
148 }
159 }

#field-passed-by-reference-rejected

Refuses when the backing field is passed by reference, which a property cannot be

refusal
Targetsymbol P:Shop.Counter.Hits
Refusesfield-passed-by-reference; every file is left unchanged
Counter.csinput
1 using System.Threading;
2
3 namespace Shop
4 {
5 public class Counter
6 {
7 private int _hits;
8
9 public int Hits
10 {
11 get => _hits;
12 set => _hits = value;
13 }
14
15 public void Record() => Interlocked.Increment(ref _hits);
16 }
17 }

#initializer-moves-to-property

The field's initialiser becomes the property's, and expression-bodied accessors are recognised

success
Targetsymbol P:Shop.Customer.Name
Customer.csmodified
11 namespace Shop
22 {
33 public class Customer
44 {
5− private string _name = "unknown";
65 private int _visits;
76
8− public string Name
9− {
10− get => _name;
11− set => _name = value;
12− }
7+ public string Name { get; set; } = "unknown";
138
149 public int Visits => _visits;
1510 }
1611 }

#no-backing-field-rejected

Refuses a property that is already an auto-property

refusal
Targetsymbol P:Shop.Sample.Name
Refusesno-backing-field; every file is left unchanged
Sample.csinput
1 namespace Shop
2 {
3 public class Sample
4 {
5 public string Name { get; set; } = "";
6 }
7 }

#not-trivial-rejected

Refuses a property whose setter does more than store the value

refusal
Targetsymbol P:Shop.Account.Balance
Refusesnot-trivial; every file is left unchanged
Account.csinput
1 using System;
2
3 namespace Shop
4 {
5 public class Account
6 {
7 private decimal _balance;
8
9 public decimal Balance
10 {
11 get => _balance;
12 set
13 {
14 if (value < 0) throw new ArgumentOutOfRangeException(nameof(value));
15 _balance = value;
16 }
17 }
18 }
19 }

#nullable-generic

A nullable property of a type parameter keeps its annotation

success
Targetsymbol P:Shop.Box`1.Item
Projectnullable enable
Box.csmodified
11 namespace Shop
22 {
33 public class Box<T>
44 {
5− private T? _item;
6−
7− public T? Item
8− {
9− get => _item;
10− set => _item = value;
11− }
5+ public T? Item { get; set; }
126 }
137 }

#partial-type-across-files

Uses of the field in another part of a partial type read the property, and the property's documentation and accessor modifiers are kept

success
Targetsymbol P:Shop.Order.Total
Order.Lines.csmodified
11 namespace Shop
22 {
33 public partial class Order
44 {
55 public void AddLine(long price)
66 {
7− _total += price;
7+ Total += price;
88 }
99 }
1010 }
Order.csmodified
11 namespace Shop
22 {
33 public partial class Order
44 {
5− // Running total, in pence.
6− private long _total;
7−
85 /// <summary>The order total.</summary>
9− public long Total
10− {
11− get => _total;
12− protected set => _total = value;
13− }
6+ public long Total { get; protected set; }
147 }
158 }

#readonly-field-assigned-in-constructor

An expression-bodied property over a readonly field becomes a get-only auto-property, assigned by the constructor

success
Targetsymbol P:Shop.Product.Code
Product.csmodified
11 namespace Shop
22 {
33 public class Product
44 {
5− private readonly string _code;
6−
75 public Product(string code)
86 {
9− _code = code;
7+ Code = code;
108 }
119
12− public string Code => _code;
10+ public string Code { get; }
1311 }
1412 }

#written-outside-constructor-gets-private-setter

A get-only property whose field the type writes after construction gets a private setter

success
Targetsymbol P:Shop.Counter.Value
Counter.csmodified
11 namespace Shop
22 {
33 public class Counter
44 {
5− private int _value;
5+ public int Value { get; private set; }
66
7− public int Value
8− {
9− get { return _value; }
10− }
11−
12− public void Increment() => _value++;
7+ public void Increment() => Value++;
138 }
149 }