A constructor is a special method that runs automatically at the moment we create an instance of a class (when we use new).
Its main mission is to initialize the object’s state so that it is valid from the very beginning of its existence.
Basic Syntax
A constructor has two unique characteristics:
- It has the same name as the class.
- It has no return type (not even
void).
For example, for the Usuario class.
public class Usuario
{
public string Nombre { get; set; }
// This is the Constructor
public Usuario()
{
Nombre = "Sin Nombre"; // Default initialization
Console.WriteLine("¡Ha nacido un usuario!");
}
}
// When doing 'new', the constructor code runs
Usuario user = new Usuario();
When we do new Usuario(), what happens in memory (Heap) is:
- First, space is allocated for the object.
- Immediately after, the constructor is called.
The default constructor
If you don’t write any constructor, C# generates an invisible one for you: the Default Constructor (parameterless). It does nothing more than initialize variables to their default values (0, null, false).
But as soon as you write your own constructor with parameters, the default constructor disappears (C# only generates it if there is no other constructor).
public class Producto
{
public string Nombre { get; set; }
public Producto(string nombre) // Own constructor
{
Nombre = nombre;
}
}
// THIS WILL CAUSE A COMPILATION ERROR
Producto p = new Producto();
The compiler will tell you: “Excuse me? You told me I need a name to create a product. You can’t create it empty.”
If you want to have both (an empty one and one with parameters), you must explicitly write the empty constructor.
Constructor Overloading
Just like with any other method, we can have multiple constructors with different signatures (parameters). This allows creating objects in different ways.
public class Rectangulo
{
public int Ancho { get; set; }
public int Alto { get; set; }
// Option 1: Empty (everything to 0)
public Rectangulo() { }
// Option 2: A square (width = height)
public Rectangulo(int lado)
{
Ancho = lado;
Alto = lado;
}
// Option 3: A specific rectangle
public Rectangulo(int ancho, int alto)
{
Ancho = ancho;
Alto = alto;
}
}
Here we have different valid constructors for Rectangulo.
Constructor Chaining
We can make one constructor call another using the this keyword. This way, we centralize the initialization logic in a single point.
public class Robot
{
public string Nombre { get; set; }
public int Bateria { get; set; }
// Base constructor that does the real work
public Robot(string nombre, int bateria)
{
Nombre = nombre;
Bateria = bateria;
}
// Calls the base constructor using 'this'
public Robot(string nombre) : this(nombre, 100)
{
// Only additional code here if necessary
}
}
When we call new Robot("R2D2"), the two-parameter constructor runs first (setting battery to 100) and then returns to the one-parameter constructor.
Object Initializers
Since C# 3.0, we have a wonderful syntax to set public properties right after creating the object, without needing to create hundreds of constructors.
// Without a specific constructor, using an Initializer
Personaje p = new Personaje
{
Nombre = "Mario",
Nivel = 1,
Vida = 100
};
This is equivalent to creating the empty object and then assigning the properties one by one, but much more readable and atomic.
Key Difference:
- The Constructor is used to ensure MANDATORY dependencies (invariants).
- The Initializer is used to configure OPTIONAL properties.
Static Constructors
Sometimes we need to initialize things that don’t belong to a specific instance, but to the class in general (static variables). For that, the static constructor exists.
- It has no access modifier (it’s not public nor private).
- It has no parameters.
- It runs only once: automatically before the first instance is created or any static member is accessed.
public class Servidor
{
public static DateTime HoraInicio;
// Runs only once in the entire application's lifetime
static Servidor()
{
HoraInicio = DateTime.Now;
Console.WriteLine("Servidor inicializado.");
}
}
It’s not very common. Generally, you don’t want to do this (unless you know exactly why you need to do it this way).
Primary Constructors
C# 12 introduced Primary Constructors for regular classes (they previously only existed in records). This allows us to drastically reduce repetitive code (“boilerplate”).
Instead of defining the constructor inside, we put the parameters directly in the class definition.
public class Servicio
{
private ILogger _logger;
public Servicio(ILogger logger)
{
_logger = logger;
}
}
// Parameters go at the top, next to the class name
public class Servicio(ILogger logger)
{
public void Log(string mensaje)
{
// We can use 'logger' directly throughout the class
logger.Log(mensaje);
}
}
It’s much cleaner; C# takes care of managing those parameters so they are available throughout the class.
