Start learning free

LESSON 152 OF 347 · C# ESSENTIALS

Properties in C#: Auto-Properties with get and set

Lesson Read the full explanation

A class is a blueprint that bundles related data into one value. So far every value you used lived in its own variable — name here, level there. A class lets you say: a Player HAS a name and a level, and every player object carries its own copies:

class Player {
    public string Name { get; set; }
    public int Level { get; set; }
}

var hero = new Player();
hero.Name = "Ana";              // runs the hidden setter
Console.WriteLine(hero.Name);   // runs the hidden getter

Look closely at public string Name { get; set; }. That is not a plain variable — it is an auto-property. The { get; set; } tail tells the compiler to generate a hidden storage field plus two tiny methods: a getter that runs whenever someone reads hero.Name, and a setter that runs whenever someone assigns to it. From the outside it reads exactly like a field, but because every access secretly goes through a method, you can later add validation or logging without changing a single caller. That is why idiomatic C# exposes data as properties, not public fields.


Computed properties — and the trap.

Now drop the set; entirely and replace the body with =>. You get a computed property with an expression body — it stores nothing at all and recomputes its value from other properties on every read:

public int Power => Level * 25;   // read-only: recomputed on every read

The => means: "when someone reads Power, evaluate this expression". Change Level and the next read of Power is automatically up to date — a computed value can never go stale.

But a computed property has a getter and NOTHING else. Try to assign to it and the compiler stops you cold:

hero.Power = 9000;
error CS0200: Property or indexer 'Player.Power' cannot be assigned to -- it is read only

Read the error literally: only a property with a set accessor may stand on the left of =. When you catch yourself wanting to SET a computed property, set the data it is computed FROM instead — raise Level, and Power follows by itself.


Assignment
Follow the Coding Tutorial and build the class both ways — stored and computed:

  • define a class Player,
  • give it two auto-properties: Name (a string) and Level (an int), each with { get; set; },
  • add a computed property Power with the expression body => Level * 25,
  • in Run, create var hero = new Player();, set Name to "Ana" and Level to 4,
  • print the name, the level and the power.

The exact expected output is:

name = Ana
level = 4
power = 100

Hint
{ get; set; } is storage the outside world can read and write; => is a value recomputed on every read. You never set Power — you set Level and let Power answer Level * 25.


STEP 1 OF 11
    Ctrl + Enter to check

    C# · C# ESSENTIALS

    Properties Get Set in C#, explained

    What a property is

    A class bundles related data into one value. A Player has a name and a level, and every Player object carries its own copies. In C# you normally expose that data as properties rather than public fields.

    A property looks like a field from the outside: you read it with hero.Name and assign to it with hero.Name = "Ana". Inside, it is a pair of small methods called accessors. The get accessor runs when someone reads the property, and the set accessor runs when someone assigns to it. Because every access goes through a method, you can add validation or other logic later without changing the code that uses the class.

    Auto-properties

    When the accessors need to do nothing special, C# lets you write them as get; and set;. The compiler then creates a hidden field to hold the value and writes both accessors for you. This is an auto-property:

    class Player {
        public string Name { get; set; }
        public int Level { get; set; }
    }
    
    var hero = new Player();
    hero.Name = "Ana";
    Console.WriteLine(hero.Name); // Ana
    

    The pattern is: access modifier, type, name, then { get; set; }. Property names are PascalCase by convention, so Name and not name.

    A new object starts with default values. For a Player created with new Player(), Name is null and Level is 0 until you assign something.

    Properties with real logic

    The accessors can also be written out. This property refuses negative values:

    class Account {
        private int balance;
    
        public int Balance {
            get { return balance; }
            set {
                if (value < 0) throw new ArgumentException("negative");
                balance = value;
            }
        }
    }
    

    Inside set, the keyword value is the value that was assigned. Callers still write account.Balance = 50;, and nothing changes for them. This is why idiomatic C# prefers properties to public fields: you can turn a plain auto-property into this version later, and all existing callers keep working.

    Restricting who can set

    You can make an accessor more restrictive or leave it out:

    • { get; private set; } can be read anywhere but assigned only inside the class.
    • { get; init; } can be assigned only when the object is created, for example with new Player { Name = "Bo" }.
    • { get; } is read-only: it can be set by an initializer on the property or in the constructor, and nowhere else.

    Computed properties

    A property doesn't have to store anything. With an expression body, written with =>, it calculates its value each time it is read:

    public int Power => Level * 25;
    

    This has no set accessor and no hidden field. Every read of Power evaluates Level * 25 using the current Level. If Level changes from 4 to 10, the next read of Power gives 250. A computed value can't go out of date, because nothing is stored.

    The trap: assigning to a computed property

    Since there is no set, you can't assign to a computed property:

    hero.Power = 9000;
    
    error CS0200: Property or indexer 'Player.Power' cannot be assigned to -- it is read only
    

    Only a property with a set accessor can appear on the left of =. When you want a different Power, change the data it is computed from: raise Level, and Power follows.

    The exercise, step by step

    The lesson asks for a Player class with two stored properties and one computed property, and a Run method that uses them.

    1. Open the class. Write class Player { }. Everything between the braces describes what every Player object carries.
    2. Add Name. A public string property with { get; set; }.
    3. Add Level. A public int property with the same accessors. Each object gets its own hidden storage for it.
    4. Add Power. A public int property with the expression body => Level * 25;. It has no braces and no set.
    5. Declare Run. A public method that returns void, placed below the class.
    6. Create the player. Inside Run, write var hero = new Player();.
    7. Set the data. Assign "Ana" to hero.Name and 4 to hero.Level. Both assignments call the setters.
    8. Print the three values with Console.WriteLine, using the exact labels from the expected output. Each read calls a getter, and reading Power computes 4 * 25.
    class Player {
        public string Name { get; set; }
        public int Level { get; set; }
        public int Power => Level * 25;
    }
    
    public void Run() {
        var hero = new Player();
        hero.Name = "Ana";
        hero.Level = 4;
        Console.WriteLine("name = " + hero.Name);
        Console.WriteLine("level = " + hero.Level);
        Console.WriteLine("power = " + hero.Power);
    }
    

    The output is:

    name = Ana
    level = 4
    power = 100
    

    Common mistakes

    • Writing a public field instead of a property. public string Name; compiles and works, but it has no accessors, so you can't add checks later without changing the member's kind.
    • Forgetting ; after an expression body. public int Power => Level * 25 without the semicolon doesn't compile.
    • Writing the accessors in the wrong form. It is { get; set; }, with semicolons inside the braces, not { get, set }.
    • Lowercase names. The lesson's names are Name, Level and Power. C# is case-sensitive, so hero.name doesn't exist.
    • Printing the labels with the wrong case or spacing. The output must match name = Ana exactly, including the spaces around =.
    • Expecting a non-null name by default. A string property that you never assign is null.

    Key takeaways

    • A property is read like a field but runs a get or set method underneath.
    • { get; set; } is an auto-property, where the compiler generates the storage.
    • Write the accessors out when you need validation, and use private set, init or no setter to limit who can assign.
    • => makes a computed, read-only property that stores nothing. To change it, change the data it depends on.
    Keep going with C#About 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free