Skip to content

Classes, objects and constructors

↑

Examples are independent C# snippets for modern .NET console projects unless noted otherwise. Class declarations and top-level code can live in Program.cs when top-level statements come first.

A class describes a reference type; an object is an instance of that class. Fields store state and methods express behavior. Constructors establish a valid initial state.

new creates an instance. Each instance owns its non-static fields. A constructor has the class name, has no return type and runs when an instance is created.

Example.cs
Player player = new("Alex");
player.AddPoints(5);
Console.WriteLine($"{player.Name}: {player.Score}");
public class Player
{
public string Name { get; }
public int Score { get; private set; }
public Player(string name) => Name = name;
public void AddPoints(int amount) => Score += amount;
}

Prefer private fields and methods that maintain object invariants. Public properties can expose read-only state without allowing arbitrary writes. A constructor should validate arguments when they are essential for a usable object.

Example.cs
public sealed class BankAccount
{
public decimal Balance { get; private set; }
public void Deposit(decimal amount)
{
if (amount <= 0) throw new ArgumentOutOfRangeException(nameof(amount));
Balance += amount;
}
}

Most projects put one main type per source file, but that is a convention rather than a C# requirement. partial allows one type declaration to span multiple files, commonly for generated code. The parts compile into the same type in the same assembly.

Example.cs
// Player.Core.cs
public partial class PlayerProfile { public string Name = "Alex"; }
// Player.Stats.cs (another file, same namespace and assembly)
public partial class PlayerProfile { public int Level = 1; }

Deep dive: separate a blueprint from a real object

Section titled “Deep dive: separate a blueprint from a real object”

A class declaration describes a type: which data each instance holds and which operations it supports. new Player("Alex") allocates an object and invokes an instance constructor to establish its initial state. Two new expressions normally produce two independent objects, even when the arguments are identical. A variable of a class type usually holds a reference to an object rather than embedding a copy of the object’s fields.

Program.cs
var first = new Player("Alex");
Player alias = first;
var second = new Player("Alex");
alias.Level = 5;
Console.WriteLine(first.Level); // 5: same object
Console.WriteLine(second.Level); // 1: a different object
public sealed class Player
{
public string Name { get; }
public int Level { get; set; } = 1;
public Player(string name) => Name = name;
}

This distinction matters when passing objects to methods: the method receives a copy of the reference, so it can mutate the same object. Reassigning its local parameter does not automatically replace the caller’s variable.

A constructor should establish a valid, useful starting state: require essential parameters, validate them, assign read-only members and avoid leaving partially initialized objects. Constructors can be overloaded; : this(...) reuses another constructor, and : base(...) invokes a base-class constructor. Avoid hiding heavy I/O or network calls inside routine constructors, because object creation then becomes expensive and difficult to test.

Fields, properties and methods have different jobs

Section titled “Fields, properties and methods have different jobs”

Use fields as internal storage, properties for controlled values, and methods for actions. A class should have one understandable responsibility rather than mixing input handling, saving files, UI drawing and combat calculations. Unity components follow the same separation, but a MonoBehaviour is normally added to a GameObject rather than instantiated using new.

One public class per descriptive .cs file is a useful convention, not a universal C# language requirement. Several classes can share one file, and partial lets pieces of one type span files; tools and generated code commonly use it. Namespaces organize names, assemblies form compilation boundaries, and folders are a project convention rather than C# access controls.

Give each class one understandable responsibility and establish required values in its constructor.

Do not confuse a class with the .cs file containing it. Public mutable fields expose state that other code can change freely.

Create a Book class whose constructor receives a title and exposes it read-only.

Reveal a solution

public class Book { public string Title { get; } public Book(string title) => Title = title; }.