GDKB

Constructors

Beginner20 Minutes readUpdated July 21, 20263 related concepts

A constructor is a special method that runs automatically when an object is created, responsible for putting that object into a valid starting state before any other code can use it. Instead of creating an object and then separately remembering to configure it correctly, a constructor guarantees every object begins life with its required data already set.

On this page

Constructors

Summary

A constructor is a special method that runs automatically when an object is created, responsible for putting that object into a valid starting state before any other code can use it. Instead of creating an object and then separately remembering to configure it correctly, a constructor guarantees every object begins life with its required data already set.

Why This Matters

Without a constructor, creating an object and initializing it are two separate steps — var weapon = new Weapon(); weapon.Damage = 10; weapon.Name = "Sword"; — and nothing stops code from skipping the second step entirely, leaving weapon.Name as null and weapon.Damage as 0. A bug like that surfaces far from where the object was created, when something finally tries to use the uninitialized data. A constructor that requires Name and Damage up front — new Weapon("Sword", 10) — makes creating an invalid object impossible to express in the first place.

Mental Model

A constructor is a factory's final assembly step, not an empty box handed to the customer. Buying furniture that arrives fully assembled means it's usable the moment it's unboxed — there's no separate "now attach the legs or it'll be unstable" step someone might forget. A class with a constructor that requires its essential data works the same way: by the time new Weapon("Sword", 10) returns, the object is already complete and safe to use, rather than handed over "empty" and hoping the caller finishes the job.

Core Explanation

  • Runs exactly once, automatically, at creation. A constructor executes the moment new ClassName(...) is called, before the resulting reference is available to any other code — there is no way to create an instance and skip its constructor.
  • Shares the class's name and has no return type. public Weapon(string name, int damage) — not even void — signals it isn't a regular method returning a value, but the special routine that builds the object itself.
  • A class can have multiple constructors (overloading). Weapon() and Weapon(string name, int damage) can coexist, giving callers a choice between a default and a fully-specified creation path (see [[CON-0027]]).
  • this(...) chains one constructor to another to avoid duplicating initialization logic. A simpler constructor can delegate to a more complete one, supplying defaults for the parameters it doesn't take.

Syntax / Theory

C# (Unity — plain classes)

csharp
public class Weapon
{
    public string Name { get; }
    public int Damage { get; }

    public Weapon(string name, int damage)
    {
        Name = name;
        Damage = damage;
    }

    // Chains to the main constructor, supplying a default damage value.
    public Weapon(string name) : this(name, damage: 10) { }
}
csharp
var sword = new Weapon("Sword", 25);
var dagger = new Weapon("Dagger"); // Uses the default-damage overload.

Note: MonoBehaviour-derived classes are created by Unity attaching them to a GameObject, not by calling new directly — use Awake() or Start() for their initialization instead of a constructor (see Common Mistakes).

C++ (Unreal)

cpp
class FWeapon
{
public:
    FWeapon(const FString& InName, int32 InDamage)
        : Name(InName), Damage(InDamage) // Member initializer list.
    {
    }

private:
    FString Name;
    int32 Damage;
};

AActor/UObject subclasses in Unreal use their own constructor for component setup and default subobject creation, but full gameplay initialization typically happens in BeginPlay(), mirroring Unity's Awake()/Start() split from a plain-class constructor.

Practical Example

A configuration object that must always have a valid file path is a good candidate for constructor-enforced initialization.

csharp
public class SaveFile
{
    public string Path { get; }
    public DateTime CreatedAt { get; }

    public SaveFile(string path)
    {
        if (string.IsNullOrEmpty(path))
            throw new ArgumentException("Save file path cannot be empty.");

        Path = path;
        CreatedAt = DateTime.Now;
    }
}
csharp
var save = new SaveFile("slot1.json"); // Guaranteed valid the moment it exists.

There is no way to end up with a SaveFile that has a null or empty Path — the constructor makes that state unrepresentable rather than relying on every caller to remember a validation step.

Common Mistakes

  • Trying to use new on a MonoBehaviour. Unity components are attached via AddComponent<T>() or exist in the scene already — a hand-called constructor on a MonoBehaviour runs, but Unity-specific setup (like accessing transform) isn't valid there yet; use Awake()/Start() instead.
  • Doing heavy work inside a constructor. Loading a file, hitting the network, or spawning other objects inside a constructor makes object creation slow and unpredictable — prefer a separate Initialize() method for anything beyond assigning fields from parameters.
  • Leaving a field uninitialized because it "will be set later." Defeats the entire purpose of a constructor — if a field truly must be set after construction, that's a signal it should be optional (with a sensible default) rather than required.
  • Duplicating initialization logic across multiple constructors instead of chaining with this(...). Two constructors that each independently set the same three fields will eventually drift out of sync when one is updated and the other is forgotten.

Game Development Example

An enemy spawner creates fully-configured enemies from data, rather than creating a blank enemy and configuring it across several follow-up calls.

csharp
public class EnemyData
{
    public string Name { get; }
    public int MaxHealth { get; }
    public float MoveSpeed { get; }

    public EnemyData(string name, int maxHealth, float moveSpeed)
    {
        Name = name;
        MaxHealth = maxHealth;
        MoveSpeed = moveSpeed;
    }
}
csharp
void SpawnWave(EnemyData data, int count)
{
    for (int i = 0; i < count; i++)
    {
        var enemy = Instantiate(enemyPrefab);
        enemy.Configure(data); // Enemy MonoBehaviour still uses Configure, not new.
    }
}

Even where Unity's object model prevents a literal constructor on the spawned MonoBehaviour, the plain EnemyData class still uses a constructor to guarantee every wave definition is complete before it's ever passed to SpawnWave.

Best Practices

  • Require every field the object cannot function without as a constructor parameter; make anything truly optional a property with a sensible default instead.
  • Keep constructor bodies limited to assignment and cheap validation — push expensive setup into an explicit Initialize()/Awake() step.
  • Use constructor chaining (this(...)) to avoid duplicating initialization logic across overloads.
  • For MonoBehaviours, treat Awake() as "the constructor equivalent" for self-contained setup, and Start() for setup that depends on other objects already existing.

Exercises

  1. Write a Vector2Int -style plain class with an X/Y constructor, then add a second constructor with no parameters that chains to the first with this(0, 0).
  2. Take the SaveFile example and add a second constructor overload that accepts a directory and a filename separately, combining them into the final Path — chain it to the existing single-path constructor.
  3. You're designing an Item class where Name and Id are always required but Description and IconPath are often left as defaults. Decide between several constructor overloads versus one constructor plus optional/default parameters, and justify the choice.
  4. Research C# object initializers (new Weapon { Name = "Sword" }) and explain why they don't replace the need for a constructor when a class has fields that must never be left at their default value.
  • Objects — a constructor is the specific mechanism by which an object moves from "not existing" to "existing in a valid state"; understanding what an object is is the necessary prerequisite.
  • Properties — constructors are the most common place inside a class allowed to set a read-only or private set property to its initial value.
  • Method Overloading — multiple constructors on one class are simply overloading applied to the constructor specifically.

Further Reading

Related Concepts