Reading — step 1 of 6
Learn
Properties Beyond Auto
Auto-properties (public int X { get; set; }) are shorthand for a hidden backing field plus trivial accessors. The moment you need validation, computed values, or asymmetric access, you write the property out in full — and that's where properties earn their keep over public fields.
Full properties and the value keyword
class Person {
private string _name = "";
public string Name {
get => _name;
set {
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("name required");
_name = value.Trim();
}
}
}
Inside a setter, value is the implicit parameter: when someone writes p.Name = "Ada", value is "Ada". The setter decides whether to store it, transform it, or reject it. That's the core idiom — callers keep plain assignment syntax, but the class enforces its invariant ("a Person always has a non-blank name") in exactly one place.
The get => ... / set => ... arrows are expression-bodied accessors — same behavior as { return _name; }, less ceremony.
Computed properties
A getter-only property derived from other state needs no backing field:
class Rectangle {
public int Width { get; }
public int Height { get; }
public int Area => Width * Height; // recomputed on every access
public Rectangle(int w, int h) {
Width = w; Height = h;
}
}
Area can never go stale because it is never stored. (Width and Height are getter-only auto-properties — assignable only inside the constructor.)
Asymmetric access
public int Count { get; private set; } // anyone reads, only this class writes
Newer C# adds init (settable only during object initialization, C# 9) and required (C# 11). Know they exist for interviews — but the compiler behind this course's grader is Mono's mcs, which stops around C# 7, so do not use them in exercises here.
Indexers
An indexer lets your class be used with [] like an array or dictionary:
class Storage {
private Dictionary<string, int> _data = new Dictionary<string, int>();
public int this[string key] {
get => _data.TryGetValue(key, out var v) ? v : 0;
set => _data[key] = value;
}
}
var s = new Storage();
s["alice"] = 30;
Console.WriteLine(s["alice"]); // 30
The traps
- Setter recursion: inside the setter, assigning to the property itself (
Name = value;) calls the setter again, forever —StackOverflowException. Always assign to the backing field:_name = value;. - Heavy work in a getter: callers assume reads are cheap and will happily call them inside loops. Getters should be fast and side-effect free.
Your exercise
Give Account a Balance property that starts at 0 and rejects negatives by throwing an ArgumentException whose message is balance cannot be negative. Main is already written: it reads an integer, assigns it, and prints balance: <n> on success or error: <message> on catch.
The grader's three tests — and the exact mistake each catches:
- Input
100expectsbalance: 100— the setter must actually storevalueinto_balanceafter validating. - Input
-5expectserror: balance cannot be negative— use the ONE-argument constructor:throw new ArgumentException("balance cannot be negative");. The two-argument formnew ArgumentException(msg, "value")appendsParameter name: valuetoe.Message, so the printed line no longer matches. The message must match exactly: lowercase, no period. - Input
0(hidden test) expectsbalance: 0— zero is a valid balance. Guard withvalue < 0, notvalue <= 0.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…