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 withnew 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.
- Open the class. Write
class Player { }. Everything between the braces describes what everyPlayerobject carries. - Add
Name. A publicstringproperty with{ get; set; }. - Add
Level. A publicintproperty with the same accessors. Each object gets its own hidden storage for it. - Add
Power. A publicintproperty with the expression body=> Level * 25;. It has no braces and noset. - Declare
Run. A public method that returnsvoid, placed below the class. - Create the player. Inside
Run, writevar hero = new Player();. - Set the data. Assign
"Ana"tohero.Nameand4tohero.Level. Both assignments call the setters. - Print the three values with
Console.WriteLine, using the exact labels from the expected output. Each read calls a getter, and readingPowercomputes 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 * 25without 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,LevelandPower. C# is case-sensitive, sohero.namedoesn't exist. - Printing the labels with the wrong case or spacing. The output must match
name = Anaexactly, 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
getorsetmethod 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,initor 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.