C#

Classes, properties, and interfaces

Auto-properties instead of hand-written getters, and what an interface promises that a base class does not.

After this lesson you can

  • Write a class with auto-implemented properties and a constructor
  • Say what an interface can require that an abstract class cannot
  • Call a method polymorphically through an interface reference

A property looks like a field from the outside and is really a pair of methods underneath.

public class Account
{
    public string Owner { get; }              // read-only after construction
    public decimal Balance { get; private set; }

    public Account(string owner, decimal balance = 0)
    {
        Owner = owner;
        Balance = balance;
    }

    public void Deposit(decimal amount) => Balance += amount;
}

{ get; private set; } is an auto-property: the compiler generates the backing field for you. Balance can be read from anywhere but only changed from inside Account itself — encapsulation without writing a private field and a public getter by hand.

Try it

A balance nothing outside the class can set directlycsharp-9
public class Account{    public string Owner { get; }    public decimal Balance { get; private set; }     public Account(string owner, decimal balance = 0)    {        Owner = owner;        Balance = balance;    }     public void Deposit(decimal amount) => Balance += amount;} public static class Solution{    public static string Describe()    {        var acc = new Account("Nino", 100);        acc.Deposit(50);        return $"{acc.Owner}: {acc.Balance}";    }}
What to look for

Interfaces

public interface IShape
{
    double Area();
}

public class Circle : IShape
{
    public double Radius { get; init; }
    public double Area() => Math.Round(Math.PI * Radius * Radius, 2);
}

public class Square : IShape
{
    public double Side { get; init; }
    public double Area() => Side * Side;
}

A C# class can implement any number of interfaces but inherit from only one base class — the same single-inheritance-for-classes, unlimited-for-interfaces split most object-oriented languages share. An interface can declare a default method body too (since C# 8), but its main job is still the contract: what a type promises to do, with no state of its own.

Polymorphism through the interface

List<IShape> shapes = new() { new Circle { Radius = 2 }, new Square { Side = 3 } };
double total = shapes.Sum(s => s.Area());

The loop — or here, Sum — never needs to know which concrete type each IShape actually is. Which Area() runs is decided at runtime by the object's real type, which is exactly what makes adding a Triangle : IShape later free at every place that already only ever depended on IShape.

Try it yourself

2 visible tests · 2 hidden tests

IShape declares Area(). Circle and Square both implement it — Circle.Area() should be Math.Round(Math.PI * Radius * Radius, 2), Square.Area() should be Side * Side. Implement TotalArea(List<double> circleRadii, List<double> squareSides): build one Circle per radius and one Square per side, and return the sum of every shape's Area(), rounded to 2 decimal places.

  • TotalArea([2], [3])
  • TotalArea([], [1,2,3])
Loading editor…

Sign up to check the hidden tests and save your progress. Sign up