Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C# & .NET (C# 14, .NET 10 LTS)  Module 6 – Classes, objects and encapsulation

Properties, indexers and the field keyword

Write C# auto-properties and init-only and computed properties, put a rule into a setter with the C# 14 field keyword, and give a type its own indexer.

  • Beginner
  • 20 minutes
  • Examples run with .NET 10.0.401 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 7 of 20 minutes free
Free: A property looks like a field and runs like a method · The field keyword puts a rule into one accessor · An indexer lets callers index your type like an array. With a pass: 3 in-depth parts and 3 interview answers, about 13 minutes.

What you will learn

  • Write auto-properties and get-only, init-only and computed properties, and choose the one that fits
  • Validate and normalise a value in a setter with the C# 14 field keyword
  • Add indexers that reach a type's items by position or by a key
  • Diagnose the warnings and silent changes that field-backed properties can bring
  • Design setters and indexers that refuse bad values and report only real changes

Before you start

What you’ll learn · Before you start

What you will learn

  • Write auto-properties and get-only, init-only and computed properties, and choose the one that fits
  • Validate and normalise a value in a setter with the C# 14 field keyword
  • Add indexers that reach a type's items by position or by a key
  • Diagnose the warnings and silent changes that field-backed properties can bring
  • Design setters and indexers that refuse bad values and report only real changes

Before you start

On this page

A Name that is a public field accepts " Ravi Iyer" with its spaces and null as well, and every caller has to remember to clean it. Made a property, Name cleans and checks each value itself, while callers still write person.Name = … as if it were a field.

You will write the kinds of property C# offers, add a rule to a setter with C# 14’s field keyword, and give a type an indexer so that callers can write hall["C7"].

A property looks like a field and runs like a method

course.Room = "C-110"; looks like an assignment to a field, but Room is a property: the assignment calls its set accessor, and reading course.Room calls its get accessor. Most accessors need no code. { get; set; } is an auto-property: A property whose accessors have no body, such as { get; set; }. The compiler declares a hidden backing field and writes accessors that only read and store the value.: the compiler declares a hidden backing field: The private field that stores a property's value. For an auto-property the compiler declares it, and from C# 14 an accessor can name it with the field keyword. and writes both accessors for you. kinds.cs gives one class five kinds of property:

kinds.cs: five kinds of property on one class C# · kinds/kinds.cs
// Five kinds of property on one class, and what code outside the class may do with each.
var course = new Course("CS101", capacity: 60) { Title = "Programming in C#" };
course.Room = "B-204";
course.Enrol(45);
Console.WriteLine($"{course.Code} {course.Title}, room {course.Room}: {course.Enrolled} of {course.Capacity}");
Console.WriteLine($"seats left: {course.SeatsLeft}");

course.Room = "C-110";
course.Enrol(10);
Console.WriteLine($"moved to {course.Room}: {course.Enrolled} enrolled, {course.SeatsLeft} seats left");

class Course
{
    public Course(string code, int capacity)
    {
        Code = code;
        Capacity = capacity;
    }

    // Get-only: the constructor sets it once.
    public string Code { get; }

    public int Capacity { get; }

    // Init-only: an object initializer sets it, and nothing can change it afterwards.
    public required string Title { get; init; }

    // Read-write: any code may read it and assign it.
    public string? Room { get; set; }

    // Readable everywhere, assigned only by code inside the class.
    public int Enrolled { get; private set; }

    // Computed: nothing is stored; the value is worked out on every read.
    public int SeatsLeft => Capacity - Enrolled;

    public void Enrol(int students) => Enrolled += students;
}

Output

CS101 Programming in C#, room B-204: 45 of 60
seats left: 15
moved to C-110: 55 enrolled, 5 seats left

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run kinds.cs

What each declaration lets the code outside the class do:

Declared as Outside code may In kinds.cs
{ get; } read it; a constructor sets it Code, Capacity
{ get; init; } read it, and set it in an object initializer Title
{ get; set; } read and assign it Room
{ get; private set; } read it; the class assigns it Enrolled
=> expression read a value worked out on each read SeatsLeft

SeatsLeft is a computed property: A property whose get accessor works out its value on every read, such as int SeatsLeft => Capacity - Enrolled. It stores nothing of its own.: it stores nothing, and it gave 15 after the first enrolment and 5 after the second, because each read subtracts Enrolled from Capacity again. A get-only property stops callers from storing another value in it, not from changing the object it returns: a List<string> behind { get; } still accepts Add, so expose IReadOnlyList<string> when callers should only read the items.

Checkpoint

Check: Kinds of property and what callers may do

  1. Question 1 of 2 A course's Title must be given when the course is created and may never change afterwards. Which declaration fits?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: public required string Title { get; init; }

    init limits assignment to the moment of creation, and required makes that assignment compulsory; together they give a value that is set once and never changes.

  2. Question 2 of 2 In kinds.cs, Capacity is 60 and Enrolled is 55 after the two enrolments. What does SeatsLeft return after one more course.Enrol(3)?

    Type a number.

    A hint

    SeatsLeft stores nothing. What does it compute at the moment it is read?

    Show the answer: Question 2 of 2

    Answer: 2 seats

    SeatsLeft is computed on every read as Capacity - Enrolled, so it follows Enrolled at once: 60 - 58 = 2.

The field keyword puts a rule into one accessor

A name should be stored without the spaces people type around it, and never as null. Before C# 14 that took a private field declared by hand and both accessors written out. The field keyword of C# 14 names the compiler’s backing field inside an accessor, so you write the accessor that needs the rule and leave the other as get;:

trim.cs: a setter that trims and refuses null C# · trim/trim.cs
// The C# 14 field keyword: a setter with a rule, and no backing field to declare.
var person = new Person("  Asha Rao ");
Console.WriteLine($"[{person.Name}]");

person.Name = "   Ravi Iyer";
Console.WriteLine($"[{person.Name}]");

try
{
    // null! silences the compiler's warning, as code that reads JSON or ignores warnings can.
    person.Name = null!;
}
catch (ArgumentNullException e)
{
    Console.WriteLine($"{e.GetType().Name}: {e.Message}");
}

Console.WriteLine($"still [{person.Name}]");

class Person
{
    // The constructor assigns the property, so its setter trims this value too.
    public Person(string name) => Name = name;

    public string Name
    {
        get;
        set => field = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
    }
}

Output

[Asha Rao]
[Ravi Iyer]
ArgumentNullException: Value cannot be null. (Parameter 'value')
still [Ravi Iyer]

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run trim.cs

The constructor assigns Name, so the setter trimmed " Asha Rao " too. When null arrived, the setter threw ArgumentNullException before storing anything, and Name kept Ravi Iyer. Step through the assignments:

Interactive · message by message Three assignments to a field-backed property Step through trim.cs. A value travels from the caller into the set accessor, and only what the accessor allows reaches the backing field.
Three assignments to a field-backed property, step 5 of 5. Messages so far: The calling code to The Name accessors: " Asha Rao "; The Name accessors to The backing field: "Asha Rao"; The calling code to The Name accessors: " Ravi Iyer"; The Name accessors to The calling code: ArgumentNullException; The backing field to The calling code: "Ravi Iyer".The calling codetrim.csThe Name accessorsget; set => field = …The backing fieldfield: "Ravi Iyer"" Asha Rao ""Asha Rao"" Ravi Iyer"ArgumentNullException"Ravi Iyer"

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The constructor assigns. new Person(" Asha Rao ") runs the constructor, and its body, Name = name, calls the set accessor with the value as typed, spaces and all.
  2. Trimmed and stored. value?.Trim() makes "Asha Rao", and field = stores it in the backing field that the compiler declared. The get accessor, written as get;, returns that field, so the program printed [Asha Rao].
  3. A second assignment. person.Name = " Ravi Iyer" goes through the same accessor, and the backing field now holds "Ravi Iyer" without the leading spaces.
  4. null is refused. person.Name = null! passes null in. value?.Trim() gives null, so ?? throws ArgumentNullException before field = runs, and the exception travels back to the caller.
  5. The old value stays. Reading Name afterwards returns "Ravi Iyer": the setter threw before it stored anything, so the property kept its previous value, as the property design guidelines ask.

field works in the get, set and init accessors of properties, not of indexers, and only from C# 14, the language version of the .NET 10 SDK. In a class that already has a member named field, the word inside an accessor now means the backing field; write this.field or @field to reach the member.

Key idea

With the field keyword, one accessor holds the rule: it normalises or refuses the value before field = stores it, so a refused value leaves the old one in place.

Checkpoint

Check: The field keyword in an accessor

  1. Question 1 of 2 person.Name holds "Ravi Iyer". A caller assigns null, catches the ArgumentNullException and reads Name. What does it get?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: "Ravi Iyer"

    A setter that refuses a value should leave the old one in place, and one that throws before assigning field does exactly that.

  2. Question 2 of 2 Where does the C# 14 keyword field refer to a compiler-declared backing field?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: In the get, set and init accessors of a property

    field is a keyword only inside property accessors, from C# 14 on, the version of the .NET 10 SDK.

An indexer lets callers index your type like an array

A cinema hall of 5 rows of 10 seats is a collection, and callers expect hall[2, 6] or hall["C7"] rather than a method name. An indexer: A member declared as this[...] that lets code use square brackets on an object, as in hall[2, 6] or hall["C7"]. It is a property whose accessors take parameters. is a property with parameters, declared as this[...]: its accessors receive the arguments written between the brackets. A type may have several indexers whose parameter lists differ:

seats.cs: a seat map with two indexers C# · seats/seats.cs
// An indexer lets code use [] on your own type: here by row and seat number, or by a label such as "C7".
var hall = new SeatMap(rows: 5, seatsPerRow: 10);
hall[2, 6] = "Asha";
hall["D1"] = "Ravi";

Console.WriteLine($"C7: {hall["C7"] ?? "free"}");
Console.WriteLine($"[3, 0]: {hall[3, 0] ?? "free"}");
Console.WriteLine($"A1: {hall["A1"] ?? "free"}");
Console.WriteLine($"booked: {hall.Booked} of {hall.Rows * hall.SeatsPerRow}");

class SeatMap
{
    private readonly string?[,] _seats;

    public SeatMap(int rows, int seatsPerRow) => _seats = new string?[rows, seatsPerRow];

    public int Rows => _seats.GetLength(0);

    public int SeatsPerRow => _seats.GetLength(1);

    public int Booked { get; private set; }

    // By position, counting from 0 as arrays do.
    public string? this[int row, int seat]
    {
        get => _seats[row, seat];
        set
        {
            if (_seats[row, seat] is null && value is not null)
            {
                Booked++;
            }

            _seats[row, seat] = value;
        }
    }

    // By the label on the seat: a row letter from A and a seat number from 1.
    public string? this[string label]
    {
        get => this[label[0] - 'A', int.Parse(label[1..]) - 1];
        set => this[label[0] - 'A', int.Parse(label[1..]) - 1] = value;
    }
}

Output

C7: Asha
[3, 0]: Ravi
A1: free
booked: 2 of 50

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run seats.cs

hall["D1"] and hall[3, 0] reached the same seat, and Booked counted 2 seats, because the string indexer only turns a label into two positions and then calls the int indexer:

Interactive · stage by stage How hall["C7"] finds its seat Step through one read of the string indexer, from the label to the array element it reaches.
How hall["C7"] finds its seat, step 5 of 5. Showing hall["C7"], The row letter ('C' - 'A' = 2), The seat number (7 - 1 = 6), hall[2, 6], The array element ("Asha"). In focus: The array element.hall["C7"]the string indexerThe row letter'C' - 'A' = 2The seat number7 - 1 = 6hall[2, 6]the int indexerThe array element"Asha"

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. A label. The caller writes hall["C7"]. The get accessor of this[string label] runs with label set to "C7".
  2. Letter to row. label[0] - 'A' subtracts the char codes: C is 2 places after A, so the row is 2, counting from 0.
  3. Number to seat. label[1..] is "7", and int.Parse(...) - 1 gives 6, because the labels count seats from 1 and the array counts from 0.
  4. The other indexer. The string indexer then calls this[2, 6], so reading and booking a seat stay written once, in the int indexer.
  5. The stored name. _seats[2, 6] holds "Asha", booked earlier with hall[2, 6] = "Asha", so the program printed C7: Asha.

Microsoft’s property design guidelines keep indexers for types that hold a logical collection of items, as a seat map does. Unlike other getters, an indexer is expected to throw when its arguments name no item.

Checkpoint

Check: Indexers and their overloads

  1. Question 1 of 2 With the label rule of seats.cs (a row letter from A, a seat number from 1), which element of _seats does hall["E10"] read?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: _seats[4, 9]

    label[0] - 'A' turns the letter into a row from 0, and int.Parse(label[1..]) - 1 turns the seat number into a position from 0.

  2. Question 2 of 2 Why does the string indexer of seats.cs call this[row, seat] instead of reading and writing _seats itself?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: So that the rule for counting bookings lives in one indexer, whichever way a seat is named

    One indexer translates the label, the other holds the rules. Booked counted 2 seats however they were named.

The field keyword's traps, reproduced: initializers, nulls and old names Pro: opens with a Pro, Premium or Ultimate pass

The field keyword saves a declaration, but three of its rules surprise code that looked finished: what a property initializer does, when the compiler warns about null, and what field means in code written before C# 14. Each is reproduced below on the .NET 10 SDK.

In this part:

  • A clamping setter and its default: a page size of 500, measured
  • CS9264: a string property that stays null, and three fixes
  • C# 14 changes what field means in old code: one file built twice

5 runnable examples (C#, Python) · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Setters that clamp, refuse and notify, with every change counted Pro: opens with a Pro, Premium or Ultimate pass

A setter decides what happens to each value it receives: store it, change it or refuse it, and whether anyone is told about the change. The order of those decisions matters, and the run below counts what two orders do with the same six requests.

In this part:

  • Clamp first, compare second: two thermostats, six requests, events counted
  • What a setter should do with each kind of value: a decision table

1 runnable example (C#) · about 3 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Fields, properties and indexers across a library boundary Pro: opens with a Pro, Premium or Ultimate pass

Inside one project a public field and a property look the same where they are used. Across a library boundary they are different members, and an indexer’s behaviour for a bad argument becomes a promise to every caller. Both are measured below.

In this part:

  • A public field becomes a property: MissingFieldException, reproduced
  • Indexers by design: labels checked, exceptions named, Item kept

4 runnable examples (C#, Python) · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Practise: a thermostat with a weekly schedule

thermostat.cs checks a Thermostat that you complete: its TargetCelsius keeps values between 10 and 32 degrees and refuses NaN, and an indexer, this[DayOfWeek day], holds a target for each day, 21 degrees to start with. The property and the indexer you start from only throw, so all 8 checks fail:

thermostat.cs: eight checks of Thermostat C# · thermostat/thermostat.cs
using System.Globalization;

// Practice: write the TargetCelsius property and the day indexer of Thermostat, then run this file until all eight
// checks pass. A target is kept between 10 and 32 degrees: a lower value becomes 10 and a higher one 32. NaN is not a
// temperature, so it is rejected. Every day of the week starts at 21 degrees.
(string Check, Func<string> Run, string Expected)[] checks =
[
    ("a new thermostat", () => Text(new Thermostat().TargetCelsius), "21"),
    ("set to 5", () => Text(new Thermostat { TargetCelsius = 5 }.TargetCelsius), "10"),
    ("set to 40", () => Text(new Thermostat { TargetCelsius = 40 }.TargetCelsius), "32"),
    ("set to 22.5", () => Text(new Thermostat { TargetCelsius = 22.5 }.TargetCelsius), "22.5"),
    ("set to NaN", () => Text(new Thermostat { TargetCelsius = double.NaN }.TargetCelsius), "throws ArgumentException"),
    ("Monday set to 35", () => Text(Monday(35)), "32"),
    ("Sunday, never set", () => Text(new Thermostat()[DayOfWeek.Sunday]), "21"),
    ("day number 7", () => Text(new Thermostat()[(DayOfWeek)7]), "throws ArgumentOutOfRangeException"),
];

int passed = 0;
foreach (var (check, run, expected) in checks)
{
    string got;
    try
    {
        got = run();
    }
    catch (Exception e)
    {
        got = $"throws {e.GetType().Name}";
    }

    bool ok = got == expected;
    if (ok)
    {
        passed++;
    }

    Console.WriteLine(ok ? $"{check}: ok" : $"{check}: FAIL, {got} instead of {expected}");
}

Console.WriteLine($"{passed} of {checks.Length} checks pass");
return passed == checks.Length ? 0 : 1;

static string Text(double celsius) => celsius.ToString(CultureInfo.InvariantCulture);

static double Monday(double celsius)
{
    var thermostat = new Thermostat();
    thermostat[DayOfWeek.Monday] = celsius;
    return thermostat[DayOfWeek.Monday];
}

class Thermostat
{
    public double TargetCelsius
    {
        get => throw new NotImplementedException();
        set => throw new NotImplementedException();
    }

    public double this[DayOfWeek day]
    {
        get => throw new NotImplementedException();
        set => throw new NotImplementedException();
    }
}

Output (exit status 1)

a new thermostat: FAIL, throws NotImplementedException instead of 21
set to 5: FAIL, throws NotImplementedException instead of 10
set to 40: FAIL, throws NotImplementedException instead of 32
set to 22.5: FAIL, throws NotImplementedException instead of 22.5
set to NaN: FAIL, throws NotImplementedException instead of throws ArgumentException
Monday set to 35: FAIL, throws NotImplementedException instead of 32
Sunday, never set: FAIL, throws NotImplementedException instead of 21
day number 7: FAIL, throws NotImplementedException instead of throws ArgumentOutOfRangeException
0 of 8 checks pass

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run thermostat.cs

Write the property and the indexer

Copy thermostat.cs to your computer. Give TargetCelsius a get; and a set that throws ArgumentException when double.IsNaN(value) and otherwise stores Math.Clamp(value, 10, 32) in field, with a default of 21. The indexer cannot use field: keep a double[] of 7 targets that all start at 21, throw ArgumentOutOfRangeException when Enum.IsDefined(day) is false, and clamp what it stores the same way. Run dotnet run thermostat.cs until it prints 8 of 8 checks pass.

Then try the questions on properties, the field keyword and indexers. They start with the idea you are weakest on, and every answer explains itself.

Practice that adapts to you

Practise this lesson

6 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 6 Who may assign a property declared public int Enrolled { get; private set; }?

    Choose one answer.

    Show the answer: Question 1 of 6

    Answer: Only code inside the class, such as the Enrol method

    private set keeps the value readable everywhere and changeable only by the class's own methods.

  2. Question 2 of 6 An Order exposes public List<string> Lines { get; } = [];. What can code outside the class still do?

    Choose one answer.

    Show the answer: Question 2 of 6

    Answer: Call order.Lines.Add("…") and change the list

    To let callers only read the items, expose IReadOnlyList<string> and change the list inside the class.

  3. Question 3 of 6 Before C# 14, what did a Name property that trims its value need, compared with the version written with field?

    Choose one answer.

    Show the answer: Question 3 of 6

    Answer: A private field declared by hand, and a get accessor written out too

    field removes the hand-written backing field and the boilerplate get accessor; the rule itself is the same code as before.

  4. Question 4 of 6 An Email property should store addresses trimmed and in lower case. Which declaration does that with field?

    Choose one answer.

    Show the answer: Question 4 of 6

    Answer: public string Email { get; set => field = value.Trim().ToLowerInvariant(); } = "";

    The set accessor is the one place every assignment passes through, so the normalising goes there, before field = stores the result.

  5. Question 5 of 6 Which member declares an indexer that callers use as map[row, seat]?

    Choose one answer.

    Show the answer: Question 5 of 6

    Answer: public string? this[int row, int seat] { get => …; set => …; }

    this[...] declares an indexer; its accessors receive the arguments between the brackets, and the type may declare several with different parameter lists.

  6. Question 6 of 6 A SeatMap needs to work out the price of a seat from its row and the show time. Should that be an indexer, price[row, show]?

    Choose one answer.

    Show the answer: Question 6 of 6

    Answer: No: make it a method such as PriceOf(row, show), because it is a calculation, not an item of a collection

    Use an indexer when callers reach an item that the type holds, and a named method when they ask it to compute something.

JSON to C# Class Turn a JSON sample into a C# class with auto-properties, then decide which of them need init, a private set or a rule.

Interview questions

Fresher · Why do C# classes expose properties instead of public fields?

A property reads like a field where it is used, but its accessors are methods, so the class keeps control. A setter can check or normalise a value, a getter can compute one, and the two can have different access, such as a public get with a private set. A property can also gain such a rule later without any change to its callers’ source, while turning a public field into a property changes the member that compiled callers refer to. The Framework design guidelines therefore allow no public or protected instance fields at all, with constants and static readonly fields as the exceptions.

  1. Fresher to mid

    A PageSize property keeps values between 1 and 100 in a field-backed setter, and its declaration ends with = 500. In production a list endpoint returns 500 rows a page. Explain why the rule did not apply, and how you would make it hold for defaults as well as for assigned values.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Fresher to mid

    An Order class exposes public List<OrderLine> Lines { get; } = []; and a colleague accepts it as read-only. Weeks later a report finds orders whose total does not match their lines, because another service added lines directly. What went wrong, and how would you expose the lines?

    Model answer, Pro. First step and follow-ups

  3. Fresher to mid

    You are designing a SeatMap type for a cinema booking library. Callers need seats by row and seat number and by labels such as "F12". Would you offer indexers, methods or both, and what should happen for a seat that does not exist?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • A property reads like a field and runs accessors: auto-properties, get-only, init-only, private-set and computed properties each let outside code do something different.
  • The field keyword of C# 14 names the compiler’s backing field inside a property accessor, so one accessor can trim, check or refuse a value while the other stays get;.
  • An indexer is a property with parameters, this[...]; a type may overload it, and one indexer can call another so that the rules live in one place.

Terms in this lesson

auto-property
A property whose accessors have no body, such as { get; set; }. The compiler declares a hidden backing field and writes accessors that only read and store the value.
backing field
The private field that stores a property's value. For an auto-property the compiler declares it, and from C# 14 an accessor can name it with the field keyword.
computed property
A property whose get accessor works out its value on every read, such as int SeatsLeft => Capacity - Enrolled. It stores nothing of its own.
indexer
A member declared as this[...] that lets code use square brackets on an object, as in hall[2, 6] or hall["C7"]. It is a property whose accessors take parameters.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.