Skip to content
Properties Beyond Auto
step 1/6

Reading — step 1 of 6

Learn

~3 min readProperties and Records

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 100 expects balance: 100 — the setter must actually store value into _balance after validating.
  • Input -5 expects error: balance cannot be negative — use the ONE-argument constructor: throw new ArgumentException("balance cannot be negative");. The two-argument form new ArgumentException(msg, "value") appends Parameter name: value to e.Message, so the printed line no longer matches. The message must match exactly: lowercase, no period.
  • Input 0 (hidden test) expects balance: 0 — zero is a valid balance. Guard with value < 0, not value <= 0.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…