Menu

Clases y objetos en C#: campos, métodos, new y referencias

Cómo declarar una clase en C#, darle campos y métodos, crear objetos con new, y qué significa que una clase sea un tipo de referencia: objetos compartidos, null y NullReferenceException, ToString e inicializadores de objetos.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Una clase es un tipo que defines tú: agrupa datos (campos) con el código que trabaja sobre esos datos (métodos). Un objeto es una instancia de una clase, creada en tiempo de ejecución con new. Una clase, muchos objetos, cada uno con sus propios valores.

Declarar una clase y crear objetos

La declaración de una clase enumera sus miembros entre llaves. Los campos contienen los datos, los métodos el comportamiento. new ClassName() reserva un objeto y te da una referencia a él.

Salida:

Maya: 120.50
Omar: 40
Withdraw 200 succeeded: False

Cada objeto tiene su propio Owner y su propio Balance. Dentro de un método, Balance significa "el saldo del objeto sobre el que se llamó este método", y por eso maya.Deposit y omar.Deposit cambian números distintos. Ese objeto implícito está disponible como this cuando necesitas nombrarlo.

Los campos que no asignas empiezan con su valor por defecto: 0 para los números, false para bool y null para los strings y los demás tipos de clase.

Aquí los miembros son public para que Main pueda llegar a ellos. Las clases reales suelen mantener los campos private y exponer propiedades, y la página de modificadores de acceso tiene las reglas. Una clase también puede declarar un constructor que asigne sus campos al crear el objeto, lo que se explica en la página de constructores.

Una clase es un tipo de referencia

Una variable de un tipo de clase no contiene el objeto. Contiene una referencia a un objeto que vive en el heap. Asignar la variable copia la referencia, así que dos variables pueden apuntar al mismo objeto.

Salida:

5
6
True
False

Aquí se ven tres consecuencias:

  • Cambiar el objeto a través de second se ve a través de first, porque solo hay un objeto.
  • Pasar un objeto a un método pasa la referencia. El método puede cambiar los campos del objeto y quien llama lo ve. (Asignar un objeto nuevo al parámetro dentro del método no afecta a la variable de quien llama; para eso hace falta ref.)
  • == entre dos instancias de clase compara referencias por defecto: "¿es este el mismo objeto?", no "¿tienen los mismos valores?". lookalike tiene datos idénticos y aun así es un objeto distinto.

Si quieres copias al asignar y comparación por valor, mira los structs, que son tipos de valor, o los records, que comparan por valor.

null y NullReferenceException

Una variable de referencia puede contener null, que significa "ningún objeto". Usar un miembro a través de null lanza NullReferenceException, la excepción en tiempo de ejecución más común en el código C#.

Salida:

True
no city on file
NullReferenceException: Address was never set
Lisbon

La solución casi nunca es capturar la excepción. Averigua por qué la referencia es null y crea el objeto antes, compruébalo antes de usarlo o usa el operador condicional null ?., que se detiene y da null en lugar de lanzar una excepción. Después, ?? aporta un valor alternativo. Los dos operadores se explican en tipos nullable.

Sobrescribir ToString

Toda clase hereda de object, que proporciona ToString(). La versión heredada devuelve el nombre completo del tipo (incluido su namespace, si lo tiene), que rara vez es lo que quieres imprimir. Sobrescríbela:

Salida:

Plain
Mug (8.50)
In cart: Mug (8.50)
Total line: Mug (8.50)

Console.WriteLine, la interpolación de strings y + con un string llaman a ToString() por ti. El depurador también lo muestra, lo que hace útil sobrescribirlo aunque tú nunca imprimas el objeto.

Inicializadores de objetos

Un inicializador de objetos asigna campos y propiedades públicos justo después de construir el objeto, en la misma expresión. Se lee mejor que una lista de asignaciones y funciona bien dentro de literales de colección.

Salida:

A12: 25 (0 extras)
A13: 40 (2 extras)

Dos detalles en la segunda entrada. Price = 40m sustituye al 25m del inicializador del campo, porque el inicializador de objetos se ejecuta después de construir el objeto. Extras = { "Parking", "Poster" } no lleva new: llama a Add sobre la lista que ya contiene el campo, en lugar de sustituirla.

Un inicializador de objetos no es un constructor. Solo puede asignar miembros accesibles desde el código que llama, y no puede obligar a que un miembro se asigne. Cuando un objeto no es válido sin ciertos valores, exígelos en un constructor.

Clases partial

La palabra clave partial divide una clase entre varios archivos. El compilador une las partes en una sola clase.

// Invoice.cs
public partial class Invoice
{
    public decimal Subtotal;
    public decimal Total() => Subtotal + Tax();
}

// Invoice.Tax.cs
public partial class Invoice
{
    private decimal Tax() => Subtotal * 0.2m;
}

Cada parte debe decir partial y estar en el mismo namespace y en el mismo proyecto. El uso habitual es el código generado: los diseñadores de WinForms, los generadores de código fuente y algunos ORM escriben una parte y te dejan la otra, así que volver a generar nunca sobrescribe tu código. Dividir en archivos una clase escrita a mano solo para que sea más corta suele esconder una clase que debería ser dos.

Errores comunes

  • Usar un objeto antes de crearlo. Un campo de tipo clase es null hasta que le asignas new .... El primer acceso a un miembro lanza NullReferenceException.
  • Esperar que la asignación copie. var b = a; con una clase da un segundo nombre para el mismo objeto. Crea un objeto nuevo y copia los valores cuando necesites uno independiente.
  • Comparar objetos con == esperando una comparación por valor. Con las clases compara referencias salvo que la clase sobrecargue == (como hace string). Compara los campos relevantes o sobrescribe Equals.
  • Imprimir un objeto y obtener el nombre de su tipo. Sobrescribe ToString().
  • Declararlo todo public. Funciona en ejemplos pequeños, pero deja que cualquier código ponga el objeto en un estado no válido, por ejemplo un saldo negativo. Mantén los campos privados y cámbialos mediante métodos que comprueben las reglas.

Preguntas frecuentes

¿Qué es una clase en C#?

Una clase es un tipo que defines tú: un grupo con nombre de datos (campos y propiedades) y comportamiento (métodos). class Account { public decimal Balance; public void Deposit(decimal amount) { Balance += amount; } } declara una. Una clase es un plano; no hace nada hasta que creas objetos a partir de ella con new.

¿Qué diferencia hay entre una clase y un objeto en C#?

La clase es la definición, escrita una sola vez en el código fuente. Un objeto es una instancia de ella, creada en tiempo de ejecución con new, con su propia copia de cada campo de instancia. new Account() dos veces da dos objetos de la misma clase con saldos independientes.

¿Cómo se crea un objeto en C#?

Usa new seguido del nombre de la clase y los argumentos del constructor: var account = new Account();. Puedes asignar los campos y propiedades públicos en la misma expresión con un inicializador de objetos: new Account { Owner = "Maya" }. La variable contiene una referencia al objeto nuevo, no el objeto en sí.

¿Por qué obtengo una NullReferenceException en C#?

Usaste un miembro (.Name, .Length, una llamada a un método) a través de una variable que contiene null, así que no hay ningún objeto sobre el que actuar. Las causas habituales son un campo que nunca se asignó (los campos de clase empiezan en null), una búsqueda que devolvió null o un método que devolvió null al fallar. Compruébalo con if (x != null), usa x?.Name o asegúrate de crear el objeto antes de usarlo.

¿Cómo sobrescribo ToString en C#?

Declara public override string ToString() en tu clase y devuelve el texto que quieras, por ejemplo public override string ToString() => $"{Name} ({Price:F2})";. Sin él, Console.WriteLine(obj) y la interpolación de strings imprimen el nombre de la clase, porque eso es lo que devuelve object.ToString().

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR